diff --git a/build.gradle b/build.gradle index fda17bd..84774b6 100644 --- a/build.gradle +++ b/build.gradle @@ -10,12 +10,13 @@ plugins { // Apply the java-library plugin to add support for Java Library id 'java' id 'jacoco' - id 'org.springframework.boot' version '3.1.3' + id 'org.springframework.boot' version '3.1.5' id 'io.spring.dependency-management' version '1.1.3' - id 'io.freefair.lombok' version '8.2.2' + id 'io.freefair.lombok' version '8.4' id 'signing' id 'maven-publish' - id "org.owasp.dependencycheck" version "8.4.0" + id "org.owasp.dependencycheck" version "8.4.2" + id "org.sonarqube" version "4.4.1.3373" } group = 'com.siliconmtn' @@ -25,12 +26,21 @@ group = 'com.siliconmtn' * For 'release' publishing, use: version = n.n.n * */ -//version = '2.0.1-SNAPSHOT' -version = '2.0.1' +//version = '2.0.2-SNAPSHOT' +version = '2.0.2' -sourceCompatibility = '17' archivesBaseName = "spacelibs-java" + +configurations { + testImplementation{ + extendsFrom compileOnly + } + compileOnly { + extendsFrom annotationProcessor + } +} + repositories { // Use Maven Central for resolving dependencies. // You can declare any Maven/Ivy/file repository here. @@ -39,46 +49,48 @@ repositories { dependencies { // This dependency is used internally, and not exposed to consumers on their own compile classpath. - implementation 'commons-beanutils:commons-beanutils:1.9.4' - implementation 'commons-io:commons-io:2.13.0' - implementation 'com.googlecode.libphonenumber:libphonenumber:8.13.19' + compileOnly 'commons-beanutils:commons-beanutils:1.9.4' + compileOnly 'commons-io:commons-io:2.13.0' + compileOnly 'com.googlecode.libphonenumber:libphonenumber:8.13.19' - implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2' - implementation 'org.apache.poi:poi:5.2.3' + compileOnly 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2' + compileOnly 'org.apache.poi:poi:5.2.3' // Spring Boot (Starters -> https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-starter) - implementation 'org.springframework.boot:spring-boot-starter' - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-aop' - implementation 'org.springframework.boot:spring-boot-starter-validation' + compileOnly 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.springframework.boot:spring-boot-starter-web' + compileOnly 'org.springframework.boot:spring-boot-starter-data-jpa' + compileOnly 'org.springframework.boot:spring-boot-starter-aop' + compileOnly 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.security:spring-security-crypto:6.1.3' - implementation 'org.hibernate:hibernate-validator:8.0.1.Final' + compileOnly 'org.springframework.security:spring-security-crypto:6.1.3' + compileOnly 'org.hibernate:hibernate-validator:8.0.1.Final' // XSS content validation/filtering - implementation 'org.owasp.encoder:encoder:1.2.3' - implementation 'org.jsoup:jsoup:1.16.1' - implementation 'com.auth0:java-jwt:4.4.0' + compileOnly 'org.owasp.encoder:encoder:1.2.3' + compileOnly 'org.jsoup:jsoup:1.16.1' + compileOnly 'com.auth0:java-jwt:4.4.0' // AWS Imports for the S3 Buckets - implementation 'software.amazon.awssdk:bom:2.19.8' - implementation 'software.amazon.awssdk:s3:2.19.8' - implementation 'software.amazon.awssdk:sns:2.19.8' + compileOnly 'software.amazon.awssdk:bom:2.19.8' + compileOnly 'software.amazon.awssdk:s3:2.19.8' + compileOnly 'software.amazon.awssdk:sns:2.19.8' // Testing - testImplementation 'org.springframework.security:spring-security-test' + testImplementation 'org.springframework.security:spring-security-test' testImplementation 'org.mockito:mockito-core:5.2.0' testImplementation 'org.mockito:mockito-inline:5.2.0' testImplementation('org.springframework.boot:spring-boot-starter-test'){ exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } //OpenAPI3 (Swagger) - implementation 'org.springdoc:springdoc-openapi-ui:1.7.0' + compileOnly 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' // https://mvnrepository.com/artifact/org.apache.pulsar/pulsar-client - implementation 'org.apache.pulsar:pulsar-client:3.1.0' + compileOnly 'org.apache.pulsar:pulsar-client:3.1.0' + testImplementation("org.junit.jupiter:junit-jupiter:5.9.2") + testRuntimeOnly('org.junit.platform:junit-platform-launcher:1.9.2') } // Prevent Spring Boot from looking for a main() @@ -111,6 +123,7 @@ javadoc { } java { + sourceCompatibility = JavaVersion.VERSION_17 withJavadocJar() withSourcesJar() } diff --git a/src/main/java/com/siliconmtn/data/util/EntityUtil.java b/src/main/java/com/siliconmtn/data/util/EntityUtil.java index 8d8d0c4..7f3503a 100644 --- a/src/main/java/com/siliconmtn/data/util/EntityUtil.java +++ b/src/main/java/com/siliconmtn/data/util/EntityUtil.java @@ -96,7 +96,7 @@ public BaseEntity dtoToEntity(BaseDTO dto, BaseEntity entity) { var entityField = entity.getClass().getDeclaredField(dtoField.getName()); if (entityField.getType() != dtoField.getType() && value != null) { - log.info(entityField.getName()); + log.debug(entityField.getName()); value = entityManager.getReference(entityField.getType(), value); if (value == null) throw new EndpointRequestException( diff --git a/src/main/java/com/siliconmtn/io/http/SMTHttpConnectionManager.java b/src/main/java/com/siliconmtn/io/http/SMTHttpConnectionManager.java index 387182c..30935e0 100644 --- a/src/main/java/com/siliconmtn/io/http/SMTHttpConnectionManager.java +++ b/src/main/java/com/siliconmtn/io/http/SMTHttpConnectionManager.java @@ -14,40 +14,48 @@ import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; + import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; -// Log4j 2.x -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.apache.pulsar.shade.org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import com.siliconmtn.data.text.StringUtil; +import com.siliconmtn.io.api.EndpointResponse; + +import lombok.extern.log4j.Log4j2; /**************************************************************************** * Title: SMTHttpConnectionManager.java * Project: SpaceLibs-Java - * Description: Wrapper class around URLs and connections. Manages the - * sessions via cookies. This allows multiple requests to be made to the same - * server using any cookies that were present - * during the initial connection as well as subsequent sessions. Allow - * for the setting of an SSLSocketFactory in support of certificate-based auth - * connections. Redirects, timeouts, headers (both request and response) are managed. - * Because we return byte[], any type of data can be retrieved + * Description: Wrapper class around URLs and connections. Manages the + * sessions via cookies. This allows multiple requests to be made to the same + * server using any cookies that were present during the initial connection as + * well as subsequent sessions. Allow for the setting of an SSLSocketFactory in + * support of certificate-based auth connections. Redirects, timeouts, headers + * (both request and response) are managed. Because we return byte[], any type + * of data can be retrieved * Copyright: Copyright (c) 2021 * Company: Silicon Mountain Technologies * * @author James Camire * @version 3.0 * @since Jan 15, 2021 - * @updates: + * @updates: Added capability to build a URL constructed requiring parameter + * replacement as well as parse/convert an EndpointResponse JSON Payload. ****************************************************************************/ @Component +@Log4j2 public class SMTHttpConnectionManager { - - + /** * Identifies the type of HTTP Connection */ @@ -59,12 +67,12 @@ public enum HttpConnectionType { * Default connection prefix if not supplied */ public static final String HTTP_CONN_PREFIX = "http://"; - + /** * Default connection prefix if not supplied */ public static final String HTTPS_CONN_PREFIX = "https://"; - + /** * Socket timeout in ms */ @@ -91,7 +99,7 @@ public enum HttpConnectionType { public static final String COOKIE_HEADER_NAME = "Set-Cookie"; /** - * String representation in the header + * String representation in the header */ public static final String COOKIE_NAME = "Cookie"; @@ -109,17 +117,17 @@ public enum HttpConnectionType { * Identifies the request method for the connection as a GET method */ public static final String HTTP_PUT_METHOD = "PUT"; - + /** * Identifies the content type for the POST method. */ public static final String REQUEST_PROPERTY_CONTENT_TYPE = "Content-Type"; - + /** * Identifies the length of the message when posting data */ public static final String REQUEST_PROPERTY_CONTENT_LENGTH = "Content-Length"; - + /** * Exception message text for use when URL is not supplied. */ @@ -127,14 +135,13 @@ public enum HttpConnectionType { /** * sslSocketFactory The SSLSocketFactory that is set on the HTTPS connection - * object just after it is instantiated and prior to any other properties being set - * for said object. If a non-https url is passed to the connect methods, the + * object just after it is instantiated and prior to any other properties being + * set for said object. If a non-https url is passed to the connect methods, the * SSLSocketFactory object will be ignored. */ private SSLSocketFactory sslSocketFactory; - + // Members - static final Logger log = LogManager.getLogger(SMTHttpConnectionManager.class); private int connectionTimeout; private boolean followRedirects = true; private Map requestHeaders; @@ -143,7 +150,7 @@ public enum HttpConnectionType { private Map headerMap; private int redirectLimit = 10; private boolean useCookieHandler = false; - + /** * Initializes the manager */ @@ -152,99 +159,108 @@ public SMTHttpConnectionManager() { cookies = new LinkedHashMap<>(); headerMap = new LinkedHashMap<>(); } - + /** * Specifies if the cookie handler should be utilized + * * @param useCookieHandler Boolean to indicate whther or not to use the provided - * cookie manager + * cookie manager */ public SMTHttpConnectionManager(boolean useCookieHandler) { this(); this.useCookieHandler = useCookieHandler; } - + /** - * Accepts an SSLSocketFactory. The SSLSocketFactory is set on an HTTPS - * connection object (HttpsURLConnection) when the connection object is - * created, provided that the requested url's protocol is https. Otherwise the + * Accepts an SSLSocketFactory. The SSLSocketFactory is set on an HTTPS + * connection object (HttpsURLConnection) when the connection object is created, + * provided that the requested url's protocol is https. Otherwise the * SSLSocketFactory object is not used. + * * @param sslSocketFactory Socket factory to utilize for SSL connections */ public SMTHttpConnectionManager(SSLSocketFactory sslSocketFactory) { this(); setSslSocketFactory(sslSocketFactory); } - + /** * Retrieves data from an HTTP server and returns the data - * @param url fully qualified URL (http://www.somedomain.com) + * + * @param url fully qualified URL (http://www.somedomain.com) * @param parameters HTTP POST data as a Map of key, value pairs * @return binary data containing information retrieved from the site - * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE. - * Defaults to POST if type is null + * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, + * TRACE. Defaults to POST if type is null * @throws IOException When data can't be retrieved, this exception is thrown */ - public byte[] getRequestData(String url, Map parameters, HttpConnectionType type) - throws IOException { - if (StringUtil.isEmpty(url)) throw new IOException(MSG_URL_REQUIRED); + public byte[] getRequestData(String url, Map parameters, HttpConnectionType type) + throws IOException { + if (StringUtil.isEmpty(url)) + throw new IOException(MSG_URL_REQUIRED); return connect(createURL(url), convertPostData(parameters), type == null ? HttpConnectionType.POST : type); } - + /** * Retrieves data from an HTTP server and returns the data - * @param url fully qualified URL (http://www.somedomain.com) + * + * @param url fully qualified URL (http://www.somedomain.com) * @param parameters HTTP POST data as a Map of key, value pairs * @return binary data containing information retrieved from the site - * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE. - * Defaults to POST if type is null + * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, + * TRACE. Defaults to POST if type is null * @throws IOException When data can't be retrieved, this exception is thrown */ - public byte[] getRequestData(URL url, Map parameters, HttpConnectionType type) - throws IOException { - if (url == null) throw new IOException(MSG_URL_REQUIRED); + public byte[] getRequestData(URL url, Map parameters, HttpConnectionType type) throws IOException { + if (url == null) + throw new IOException(MSG_URL_REQUIRED); return connect(url, convertPostData(parameters), type == null ? HttpConnectionType.POST : type); } - - /** - * Retrieves data from an end point via the provided HTTP Request Type - * (GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE). Returns data as a byte array since the - * requested data may be binary - * @param url Url to call. - * @param data Data sent in the body of the message - * Do NOT include the ? in the Url or paramters - * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE - * @return byte data from the end point server - * @throws IOException - */ - public byte[] getRequestData(URL url, byte[] data, HttpConnectionType type) - throws IOException { - if (url == null) throw new IOException(MSG_URL_REQUIRED); - return connect(url, data == null ? new byte[0] : data, type == null ? HttpConnectionType.POST : type); - } - - /** - * Retrieves data from an end point via the provided HTTP Request Type - * (GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE). Returns data as a byte array since the - * requested data may be binary - * @param url Url to call. - * @param data Data sent in the body of the message - * Do NOT include the ? in the Url or paramters - * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE - * @return byte data from the end point server - * @throws IOException - */ - - public byte[] getRequestData(String url, byte[] data, HttpConnectionType type) - throws IOException { - if (url == null) throw new IOException(MSG_URL_REQUIRED); - return connect(createURL(url), data == null ? new byte[0] : data, type == null ? HttpConnectionType.POST : type); - } - + + /** + * Retrieves data from an end point via the provided HTTP Request Type (GET, + * POST, HEAD, OPTIONS, PUT, DELETE, TRACE). Returns data as a byte array since + * the requested data may be binary + * + * @param url Url to call. + * @param data Data sent in the body of the message Do NOT include the ? in the + * Url or paramters + * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE + * @return byte data from the end point server + * @throws IOException + */ + public byte[] getRequestData(URL url, byte[] data, HttpConnectionType type) throws IOException { + if (url == null) + throw new IOException(MSG_URL_REQUIRED); + return connect(url, data == null ? new byte[0] : data, type == null ? HttpConnectionType.POST : type); + } + + /** + * Retrieves data from an end point via the provided HTTP Request Type (GET, + * POST, HEAD, OPTIONS, PUT, DELETE, TRACE). Returns data as a byte array since + * the requested data may be binary + * + * @param url Url to call. + * @param data Data sent in the body of the message Do NOT include the ? in the + * Url or paramters + * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE + * @return byte data from the end point server + * @throws IOException + */ + + public byte[] getRequestData(String url, byte[] data, HttpConnectionType type) throws IOException { + if (url == null) + throw new IOException(MSG_URL_REQUIRED); + return connect(createURL(url), data == null ? new byte[0] : data, + type == null ? HttpConnectionType.POST : type); + } /** * Connects to a HTTP server using the supplied URL and gets the data + * * @param actionUrl - * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE. + * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, + * TRACE. * @return data for the request * @throws IOException When data can't be retrieved, this exception is thrown */ @@ -258,33 +274,39 @@ private byte[] connect(URL actionUrl, byte[] postDataBytes, HttpConnectionType t } } - + return baos.toByteArray(); } - + /** - * Connects to the end device and returns a stream so the data can be - * processed sequentially. - * @param url URL for the connection + * Connects to the end device and returns a stream so the data can be processed + * sequentially. + * + * @param url URL for the connection * @param params Post data to pass as a Map key, value pairs - * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE. + * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, + * TRACE. * @return Connection stream to the data * @throws IOException When data can't be retrieved, this exception is thrown */ - public InputStream getConnectionStream(URL url, Map params, HttpConnectionType type) throws IOException { + public InputStream getConnectionStream(URL url, Map params, HttpConnectionType type) + throws IOException { return connectStream(url, convertPostData(params), 0, type); } - + /** - * Connects to the end device and returns a stream so the data can be processed sequentially + * Connects to the end device and returns a stream so the data can be processed + * sequentially + * * @param actionUrl URL for the connection - * @param postData Post data to pass - * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE. + * @param postData Post data to pass + * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, + * TRACE. * @return Stream of the connection * @throws IOException When data can't be retrieved, this exception is thrown */ - private InputStream connectStream(URL actionUrl, byte[] postDataBytes, int redirectAttempt, HttpConnectionType type) - throws IOException { + private InputStream connectStream(URL actionUrl, byte[] postDataBytes, int redirectAttempt, HttpConnectionType type) + throws IOException { log.debug("Connecting to: {}", actionUrl); // build connection @@ -293,8 +315,9 @@ private InputStream connectStream(URL actionUrl, byte[] postDataBytes, int redir // execute the connection executeConnection(conn, postDataBytes, type); - //see if we need to follow a redirect - if (followRedirects && (HttpURLConnection.HTTP_MOVED_PERM == responseCode || HttpURLConnection.HTTP_MOVED_TEMP == responseCode) && redirectAttempt < redirectLimit) { + // see if we need to follow a redirect + if (followRedirects && (HttpURLConnection.HTTP_MOVED_PERM == responseCode + || HttpURLConnection.HTTP_MOVED_TEMP == responseCode) && redirectAttempt < redirectLimit) { String redirUrl = conn.getHeaderField("Location"); log.debug("Following redirect to: {}", redirUrl); if (!StringUtil.isEmpty(redirUrl)) { @@ -305,33 +328,37 @@ private InputStream connectStream(URL actionUrl, byte[] postDataBytes, int redir log.debug("Response code: {}", responseCode); - // return the response stream from the server - if the request failed return the error stream + // return the response stream from the server - if the request failed return the + // error stream return (200 <= responseCode && 300 > responseCode) ? conn.getInputStream() : conn.getErrorStream(); } - + /** - * Validates and creates a URL using actionUrl. Default prototcal is http:// + * Validates and creates a URL using actionUrl. Default prototcal is http:// + * * @param actionUrl Creates a URL object form the string url * @return URL object representing the string url - * @throws IOException When data can't be retrieved, this exception is thrown + * @throws IOException When data can't be retrieved, this exception is thrown */ public URL createURL(String actionUrl) throws IOException { - if (StringUtil.isEmpty(actionUrl)) throw new IOException("Invalid URL"); - if (! actionUrl.startsWith("http")) { + if (StringUtil.isEmpty(actionUrl)) + throw new IOException("Invalid URL"); + if (!actionUrl.startsWith("http")) { actionUrl = ((sslSocketFactory == null) ? HTTP_CONN_PREFIX : HTTPS_CONN_PREFIX) + actionUrl; } - + return new URL(actionUrl); } - + /** * Returns a connection type based on the URL protocol + * * @param url Pointer to the end server * @return Http URL Connection to the end server - * @throws IOException When data can't be retrieved, this exception is thrown + * @throws IOException When data can't be retrieved, this exception is thrown */ private HttpURLConnection createConnection(URL url) throws IOException { - // Set the cookie handler. Since this is and accessed + // Set the cookie handler. Since this is and accessed // via static method lack of use still requires action to be taken if (useCookieHandler) { CookieHandler.setDefault(new CookieManager()); @@ -341,22 +368,25 @@ private HttpURLConnection createConnection(URL url) throws IOException { // build connection if ("https".equalsIgnoreCase(url.getProtocol())) { HttpsURLConnection sConn = (HttpsURLConnection) url.openConnection(); - if (sslSocketFactory != null) sConn.setSSLSocketFactory(sslSocketFactory); + if (sslSocketFactory != null) + sConn.setSSLSocketFactory(sslSocketFactory); return sConn; } else { return (HttpURLConnection) url.openConnection(); } } - /** * Initializes and executes the connection - * @param conn COnnection to the end server + * + * @param conn COnnection to the end server * @param postData data to send to the server - * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE. - * @throws IOException When data can't be retrieved, this exception is thrown + * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, + * TRACE. + * @throws IOException When data can't be retrieved, this exception is thrown */ - private void executeConnection(HttpURLConnection conn, byte[] postDataBytes, HttpConnectionType type) throws IOException { + private void executeConnection(HttpURLConnection conn, byte[] postDataBytes, HttpConnectionType type) + throws IOException { // Setup the connection parameters initConnection(conn, postDataBytes, type); @@ -364,24 +394,26 @@ private void executeConnection(HttpURLConnection conn, byte[] postDataBytes, Htt conn.connect(); responseCode = conn.getResponseCode(); - //Parse header information + // Parse header information storeCookies(conn); } - + /** * Initializes the Connection parameters - * @param conn Connecton to the server to be initialized + * + * @param conn Connecton to the server to be initialized * @param postData Data to post to the end server - * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE. - * @throws IOException When data can't be retrieved, this exception is thrown + * @param type Request Type. One of GET, POST, HEAD, OPTIONS, PUT, DELETE, + * TRACE. + * @throws IOException When data can't be retrieved, this exception is thrown */ - private void initConnection(HttpURLConnection conn, byte[] postDataBytes, HttpConnectionType type) - throws IOException { + private void initConnection(HttpURLConnection conn, byte[] postDataBytes, HttpConnectionType type) + throws IOException { // set additional common connection properties conn.setDoOutput(true); conn.setReadTimeout(connectionTimeout > 0 ? connectionTimeout : DEFAULT_SOCKET_TIMEOUT); conn.setConnectTimeout(connectionTimeout > 0 ? connectionTimeout : DEFAULT_SOCKET_TIMEOUT); - conn.setUseCaches (false); + conn.setUseCaches(false); conn.setAllowUserInteraction(false); HttpURLConnection.setFollowRedirects(followRedirects); conn.setInstanceFollowRedirects(followRedirects); @@ -397,41 +429,43 @@ private void initConnection(HttpURLConnection conn, byte[] postDataBytes, HttpCo conn.setRequestMethod(type.toString()); if (HttpConnectionType.POST.equals(type) || HttpConnectionType.PUT.equals(type)) { - if (! requestHeaders.containsKey(REQUEST_PROPERTY_CONTENT_TYPE)) - conn.setRequestProperty(REQUEST_PROPERTY_CONTENT_TYPE,"application/x-www-form-urlencoded"); - + if (!requestHeaders.containsKey(REQUEST_PROPERTY_CONTENT_TYPE)) + conn.setRequestProperty(REQUEST_PROPERTY_CONTENT_TYPE, "application/x-www-form-urlencoded"); + conn.setRequestProperty(REQUEST_PROPERTY_CONTENT_LENGTH, Integer.toString(postDataBytes.length)); try (DataOutputStream out = new DataOutputStream(conn.getOutputStream())) { out.write(postDataBytes); } } } - + /** - * Parses the returned Set-Cookie parameter in the header into name value - * pairs and stores them in a hash map to be used during future connections. + * Parses the returned Set-Cookie parameter in the header into name value pairs + * and stores them in a hash map to be used during future connections. + * * @param conn Connection to the server to retrieve / assign cookies */ void storeCookies(HttpURLConnection conn) { - //Loop all of the HTTP header info + // Loop all of the HTTP header info int c = 0; while (conn.getHeaderField(c) != null) { // Store each header param in the headerMap collection String key = conn.getHeaderFieldKey(c); String value = StringUtil.defaultString(conn.getHeaderField(c)); headerMap.put(key, value); - + // Find the Set-Cookie parameters if (COOKIE_HEADER_NAME.equalsIgnoreCase(key)) { // Parse out the data int length = value.indexOf(COOKIE_DELIMITER); - if (length < 0) length = value.length(); + if (length < 0) + length = value.length(); value = value.substring(0, length); // Parse out the name/value pairs int sepVal = value.indexOf(COOKIE_VALUE_DELIMITER); if (sepVal > -1) { - String valueKey = value.substring(0,sepVal); + String valueKey = value.substring(0, sepVal); String valueVal = value.substring(sepVal + 1, value.length()); addCookie(valueKey, valueVal); } @@ -443,52 +477,58 @@ void storeCookies(HttpURLConnection conn) { // Add the Response Code headerMap.put(RESPONSE_CODE, Integer.toString(responseCode)); } - + /** * Converts the map data into a url encoded string and converts to a byte[] + * * @param postData Converts map of data elements into a delimited string * @return URL encoded data parameters */ public byte[] convertPostData(Map postData) { - if (postData == null || postData.isEmpty()) return new byte[0]; + if (postData == null || postData.isEmpty()) + return new byte[0]; StringBuilder sb = new StringBuilder(512); - - //convert the postData Map to URL-encoded UTF-8 key=value pairs + + // convert the postData Map to URL-encoded UTF-8 key=value pairs for (Entry entry : postData.entrySet()) { - if(sb.length() > 0) sb.append('&'); - - if(entry.getValue() instanceof Object[] || entry.getValue() instanceof Collection) + if (sb.length() > 0) + sb.append('&'); + + if (entry.getValue() instanceof Object[] || entry.getValue() instanceof Collection) listParams(sb, entry); else { String value = StringUtil.defaultString(entry.getValue() + "", ""); sb.append(entry.getKey()).append("=").append(URLEncoder.encode(value, StandardCharsets.UTF_8)); } } - + return sb.toString().getBytes(StandardCharsets.UTF_8); } /** * Helper method ensures that multiple values for a given key get added * correctly. - * @param sb assigns key values into the string builder + * + * @param sb assigns key values into the string builder * @param entry items to assign */ private void listParams(StringBuilder sb, Entry entry) { Object raw = entry.getValue(); String key = entry.getKey(); - Object[] values = (raw instanceof Collection) ? ((Collection)raw).toArray() : (Object[]) raw; + Object[] values = (raw instanceof Collection) ? ((Collection) raw).toArray() : (Object[]) raw; int i = 0; - for(Object o : values) { - if(i > 0) sb.append("&"); + for (Object o : values) { + if (i > 0) + sb.append("&"); sb.append(key).append('=').append(URLEncoder.encode(o.toString(), StandardCharsets.UTF_8)); i++; } } - + /** * Sets the factory for the SSL connection + * * @param sslSocketFactory the sslSocketFactory to set */ public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) { @@ -497,6 +537,7 @@ public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) { /** * Returns the socket factory + * * @return sslSocketFactory assigned */ public SSLSocketFactory getSslSocketFactory() { @@ -505,6 +546,7 @@ public SSLSocketFactory getSslSocketFactory() { /** * Gets the connection timeout + * * @return the connectionTimeout */ public int getConnectionTimeout() { @@ -513,6 +555,7 @@ public int getConnectionTimeout() { /** * Determines if this class will follow redirects + * * @return the followRedirects */ public boolean isFollowRedirects() { @@ -521,6 +564,7 @@ public boolean isFollowRedirects() { /** * Gets the request headers + * * @return the requestHeaders */ public Map getRequestHeaders() { @@ -529,6 +573,7 @@ public Map getRequestHeaders() { /** * Gets the onnection response code + * * @return the responseCode */ public int getResponseCode() { @@ -537,6 +582,7 @@ public int getResponseCode() { /** * Gets the cookies + * * @return the cookies */ public Map getCookies() { @@ -545,6 +591,7 @@ public Map getCookies() { /** * Gets the header maps + * * @return the headerMap */ public Map getHeaderMap() { @@ -553,6 +600,7 @@ public Map getHeaderMap() { /** * Number of redirects to foloow + * * @return the redirectLimit */ public int getRedirectLimit() { @@ -561,6 +609,7 @@ public int getRedirectLimit() { /** * Determines if cookie handler is in use + * * @return the useCookieHandler */ public boolean isUseCookieHandler() { @@ -569,6 +618,7 @@ public boolean isUseCookieHandler() { /** * Sets the connection timeout + * * @param connectionTimeout the connectionTimeout to set */ public void setConnectionTimeout(int connectionTimeout) { @@ -577,6 +627,7 @@ public void setConnectionTimeout(int connectionTimeout) { /** * Sets whether redirects should be followed + * * @param followRedirects the followRedirects to set */ public void setFollowRedirects(boolean followRedirects) { @@ -585,35 +636,42 @@ public void setFollowRedirects(boolean followRedirects) { /** * Sets request headers for the connection + * * @param requestHeaders the requestHeaders to set */ public void setRequestHeaders(Map requestHeaders) { - if (requestHeaders == null) this.requestHeaders.clear(); - else this.requestHeaders = requestHeaders; + if (requestHeaders == null) + this.requestHeaders.clear(); + else + this.requestHeaders = requestHeaders; } - + /** * Adds any request headers assigned to the request connection + * * @param conn Connection to the end server */ protected void setRequestHeaders(HttpURLConnection conn) { - if (requestHeaders.isEmpty()) return; - for (Map.Entry entry: requestHeaders.entrySet()) + if (requestHeaders.isEmpty()) + return; + for (Map.Entry entry : requestHeaders.entrySet()) conn.setRequestProperty(entry.getKey(), entry.getValue()); } - + /** * Adds a request header that will be passed on the request + * * @param key * @param value */ public void addRequestHeader(String key, String value) { requestHeaders.put(key, value); } - + /** * Adds a header map value for the HTTP connection - * @param key Cookie unique identifier + * + * @param key Cookie unique identifier * @param value Cookie value */ public void addCookie(String key, String value) { @@ -622,25 +680,30 @@ public void addCookie(String key, String value) { /** * Map containing multiple cookies to add + * * @param cookies the cookies to set */ public void setCookies(Map cookies) { - if (cookies == null) this.cookies.clear(); - else this.cookies = cookies; + if (cookies == null) + this.cookies.clear(); + else + this.cookies = cookies; } - + /** - * Adds any cookies from the collection of stored cookies and formats - * the data into: Cookie: name=value; name=value; - * Do not use this to initially add cookies to the cookie map on this object, rather - * use the addCookie(key,value) method. + * Adds any cookies from the collection of stored cookies and formats the data + * into: Cookie: name=value; name=value; Do not use this to initially add + * cookies to the cookie map on this object, rather use the addCookie(key,value) + * method. + * * @param conn Connection to the end server */ void assignCookies(HttpURLConnection conn) { - if (cookies.isEmpty() || conn == null) return; + if (cookies.isEmpty() || conn == null) + return; StringBuilder sb = new StringBuilder(250); - for (Map.Entry entry: cookies.entrySet()) + for (Map.Entry entry : cookies.entrySet()) sb.append(entry.getKey()).append(COOKIE_VALUE_DELIMITER).append(entry.getValue()).append(COOKIE_DELIMITER); conn.addRequestProperty(COOKIE_NAME, sb.toString()); @@ -648,15 +711,19 @@ void assignCookies(HttpURLConnection conn) { /** * Adds parameters to the header map + * * @param headerMap the headerMap to set */ public void setHeaderMap(Map headerMap) { - if (headerMap == null) this.headerMap.clear(); - else this.headerMap = headerMap; + if (headerMap == null) + this.headerMap.clear(); + else + this.headerMap = headerMap; } /** * Number of redirects to follow before quitting + * * @param redirectLimit the redirectLimit to set */ public void setRedirectLimit(int redirectLimit) { @@ -665,9 +732,103 @@ public void setRedirectLimit(int redirectLimit) { /** * Sets whether the cookie handler should be used + * * @param useCookieHandler the useCookieHandler to set */ public void setUseCookieHandler(boolean useCookieHandler) { this.useCookieHandler = useCookieHandler; } + + /** + * Build a URI without the need for Path Parameter Replacement + * @param endpoint + * @param urlPath + * @return + * @throws IllegalArgumentException + */ + public String buildUri(String endpoint, String urlPath) throws IllegalArgumentException { + return this.buildUriWithParams(endpoint, urlPath, null); + } + + /** + * Build the Uri that requires Path Parameter Replacement + * + * @param endpoint + * @param urlPath + * @param urlParams + * @return Url String with all placeholders replaced with urlParams + * @throws OEException + */ + public String buildUriWithParams(String endpoint, String urlPath, List urlParams) + throws IllegalArgumentException { + if (StringUtils.isEmpty(endpoint)) { + throw new IllegalArgumentException("Unable to build URI with missing endpoint"); + } else if (StringUtils.isEmpty(urlPath)) { + throw new IllegalArgumentException("Unable to build URI with missing urlPath"); + } + + UriComponents uri = UriComponentsBuilder.fromUriString(endpoint).path(urlPath).build(); + if (urlParams != null) { + uri = uri.expand(urlParams.toArray()); + } + return uri.toString(); + } + + /** + * Convert the response from the Network call into a RoadmapProcessingDTO, throw + * an exception otherwise. + * + * @param data + * @param returnType + * @param mapper + * @return The payload of the EndpointResponse converted to the returnType + * passed. + * @throws OEException + * @throws IOException + */ + public T convertEndpointResponse(byte[] data, Class returnType, ObjectMapper mapper) + throws IllegalArgumentException, IOException { + if (data == null || data.length == 0) { + throw new IllegalArgumentException("Cannot process empty data"); + } else if (returnType == null) { + throw new IllegalArgumentException("No valid returnType was passed. Unable to convert response data."); + } else if (mapper == null) { + throw new IllegalArgumentException("Missing mapper to perform conversion of data"); + } + EndpointResponse res = mapper.readValue(data, EndpointResponse.class); + if (res.isSuccess()) { + return mapper.convertValue(res.getData(), returnType); + } else { + throw new IOException("There was a problem processing the desired data: " + res.getFailedValidations()); + } + } + + /** + * Convert the response from the Network call into a RoadmapProcessingDTO, throw + * an exception otherwise. + * + * @param data + * @param returnType + * @param mapper + * @return The payload of the EndpointResponse converted to the returnType + * passed. + * @throws OEException + * @throws IOException + */ + public List convertEndpointResponseList(byte[] data, TypeReference> returnType, ObjectMapper mapper) + throws IllegalArgumentException, IOException { + if (data == null || data.length == 0) { + throw new IllegalArgumentException("Cannot process empty data"); + } else if (returnType == null) { + throw new IllegalArgumentException("No valid returnType was passed. Unable to convert response data."); + } else if (mapper == null) { + throw new IllegalArgumentException("Missing mapper to perform conversion of data"); + } + EndpointResponse res = mapper.readValue(data, EndpointResponse.class); + if (res.isSuccess()) { + return mapper.convertValue(res.getData(), returnType); + } else { + throw new IOException("There was a problem processing the desired data: " + res.getFailedValidations()); + } + } } diff --git a/src/test/java/com/siliconmtn/io/http/SMTHttpConnectionManagerTest.java b/src/test/java/com/siliconmtn/io/http/SMTHttpConnectionManagerTest.java index 92ce598..368d58d 100644 --- a/src/test/java/com/siliconmtn/io/http/SMTHttpConnectionManagerTest.java +++ b/src/test/java/com/siliconmtn/io/http/SMTHttpConnectionManagerTest.java @@ -1,5 +1,17 @@ package com.siliconmtn.io.http; +// JUnit5 +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +/// Mockito +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + // JDK 11.x import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -10,27 +22,28 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; + import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; -// JUnit5 -import static org.junit.jupiter.api.Assertions.*; +// Apache IOUtils 1.3.2 +import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; - +import org.springframework.http.HttpStatus; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.siliconmtn.data.bean.GenericVO; +import com.siliconmtn.data.exception.InvalidDataException; +import com.siliconmtn.io.api.EndpointResponse; // Libs import com.siliconmtn.io.http.SMTHttpConnectionManager.HttpConnectionType; -/// Mockito -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.mock; - -// Apache IOUtils 1.3.2 -import org.apache.commons.io.IOUtils; - /**************************************************************************** * Title: SMTHttpConnectionManagerTest.java * Project: SpaceLibs-Java @@ -44,7 +57,7 @@ * @updates: ****************************************************************************/ class SMTHttpConnectionManagerTest { - + /** * Request parameters for the test */ @@ -59,46 +72,46 @@ class SMTHttpConnectionManagerTest { @BeforeAll static void setUpBeforeClass() throws Exception { params = new HashMap<>(); - + List values = new ArrayList<>(); values.add("one"); values.add("two"); values.add("three"); - + params.put("counter", values); - params.put("arrCtr", new String[] {"alpha", "brave", "charlie"}); - params.put("name","James"); - params.put("age","30"); + params.put("arrCtr", new String[] { "alpha", "brave", "charlie" }); + params.put("name", "James"); + params.put("age", "30"); } - + @BeforeEach void setUpBeforeEach() throws Exception { - + // Instantiate the conn mgr connection = new SMTHttpConnectionManager(); - + // Assign the headers headers = new HashMap<>(); headers.put("Host", "www.siliconmtn.com"); headers.put("Referer", "www.google.com"); headers.put("User-Agent", "Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail"); - + // Assign the cookies cookies = new HashMap<>(); cookies.put("JSESSION_ID", "12345678"); cookies.put("AWSALB", "AWS_COOKIE"); cookies.put("AWSALBCORS", "AWS_COR_COOKIE"); } - + /** * Initializes and turns cookie management on and off */ @Test void testSMTHttpConnectionManagerBoolean() { - + SMTHttpConnectionManager newConn = new SMTHttpConnectionManager(true); assertTrue(newConn.isUseCookieHandler()); - + newConn = new SMTHttpConnectionManager(false); assertFalse(newConn.isUseCookieHandler()); } @@ -112,21 +125,23 @@ void testSMTHttpConnectionManagerSSLSocketFactory() { } /** - * Retrieves the data form the end server. Needs an http mock + * Retrieves the data form the end server. Needs an http mock */ @Test void testGetRequestData() throws Exception { - + // Valid the exception when null is passed String nullUrl = null; URL nullURL = null; Map nullBodyMap = null; assertThrows(IOException.class, () -> connection.getRequestData(nullUrl, nullBodyMap, HttpConnectionType.GET)); - assertThrows(IOException.class, () -> connection.getRequestData(nullURL, nullBodyMap, HttpConnectionType.GET)); - byte[] nullBodyBytes = null; - assertThrows(IOException.class, () -> connection.getRequestData(nullURL, nullBodyBytes, HttpConnectionType.GET)); - //assertThrows(IOException.class, () -> connection.getRequestData(nullUrl, nullBodyBytes, HttpConnectionType.GET)); - + assertThrows(IOException.class, () -> connection.getRequestData(nullURL, nullBodyMap, HttpConnectionType.GET)); + byte[] nullBodyBytes = null; + assertThrows(IOException.class, + () -> connection.getRequestData(nullURL, nullBodyBytes, HttpConnectionType.GET)); + // assertThrows(IOException.class, () -> connection.getRequestData(nullUrl, + // nullBodyBytes, HttpConnectionType.GET)); + // Tests with a URL Class URL mockUrl = mock(URL.class, Mockito.withSettings().useConstructor("http://www.siliconmtn.com")); mockUrlConn = mock(HttpURLConnection.class); @@ -135,14 +150,15 @@ void testGetRequestData() throws Exception { when(mockUrlConn.getInputStream()).thenReturn(mis); when(mockUrlConn.getErrorStream()).thenReturn(mis); doReturn(200).when(mockUrlConn).getResponseCode(); - assertEquals("Hello World", new String(connection.getRequestData(mockUrl, nullBodyMap, HttpConnectionType.GET))); + assertEquals("Hello World", + new String(connection.getRequestData(mockUrl, nullBodyMap, HttpConnectionType.GET))); } - /**Cookie - * Retrieves the data from the end server. Needs an http mock + /** + * Cookie Retrieves the data from the end server. Needs an http mock */ @Test - void testGetRequestDataStringMap () throws Exception { + void testGetRequestDataStringMap() throws Exception { URL mockUrl = mock(URL.class, Mockito.withSettings().useConstructor("http://www.siliconmtn.com")); connection = Mockito.spy(connection); Mockito.doReturn(mockUrl).when(connection).createURL(url); @@ -155,12 +171,12 @@ void testGetRequestDataStringMap () throws Exception { Map nullBodyMap = null; assertEquals("Hello World", new String(connection.getRequestData(url, nullBodyMap, HttpConnectionType.GET))); } - - /**Cookie - * Retrieves the data from the end server. Needs an http mock + + /** + * Cookie Retrieves the data from the end server. Needs an http mock */ @Test - void testGetRequestDataStringBytes () throws Exception { + void testGetRequestDataStringBytes() throws Exception { URL mockUrl = mock(URL.class, Mockito.withSettings().useConstructor("http://www.siliconmtn.com")); connection = Mockito.spy(connection); Mockito.doReturn(mockUrl).when(connection).createURL(url); @@ -173,12 +189,12 @@ void testGetRequestDataStringBytes () throws Exception { byte[] nullBodyBytes = null; assertEquals("Hello World", new String(connection.getRequestData(url, nullBodyBytes, HttpConnectionType.GET))); } - + /** - * Retrieves the data from the end server. Needs an http mock + * Retrieves the data from the end server. Needs an http mock */ @Test - void testGetRequestDataStringNullsMap () throws Exception { + void testGetRequestDataStringNullsMap() throws Exception { connection.setConnectionTimeout(1000); URL mockUrl = mock(URL.class, Mockito.withSettings().useConstructor("http://www.siliconmtn.com")); connection = Mockito.spy(connection); @@ -193,12 +209,12 @@ void testGetRequestDataStringNullsMap () throws Exception { Map nullBodyMap = null; assertEquals("Hello World", new String(connection.getRequestData(url, nullBodyMap, null))); } - + /** - * Retrieves the data from the end server. Needs an http mock + * Retrieves the data from the end server. Needs an http mock */ @Test - void testGetRequestDataStringNullsBytes () throws Exception { + void testGetRequestDataStringNullsBytes() throws Exception { connection.setConnectionTimeout(1000); URL mockUrl = mock(URL.class, Mockito.withSettings().useConstructor("http://www.siliconmtn.com")); connection = Mockito.spy(connection); @@ -213,12 +229,12 @@ void testGetRequestDataStringNullsBytes () throws Exception { byte[] nullBodyBytes = null; assertEquals("Hello World", new String(connection.getRequestData(url, nullBodyBytes, null))); } - + /** - * Retrieves the data from the end server. Needs an http mock + * Retrieves the data from the end server. Needs an http mock */ @Test - void testGetRequestDataStringPutMap () throws Exception { + void testGetRequestDataStringPutMap() throws Exception { headers.put(SMTHttpConnectionManager.REQUEST_PROPERTY_CONTENT_TYPE, "text/html"); connection.setRequestHeaders(headers); connection.setConnectionTimeout(1000); @@ -235,12 +251,12 @@ void testGetRequestDataStringPutMap () throws Exception { Map nullBodyMap = null; assertEquals("Hello World", new String(connection.getRequestData(url, nullBodyMap, HttpConnectionType.PUT))); } - + /** - * Retrieves the data from the end server. Needs an http mock + * Retrieves the data from the end server. Needs an http mock */ @Test - void testGetRequestDataStringPutBytes () throws Exception { + void testGetRequestDataStringPutBytes() throws Exception { headers.put(SMTHttpConnectionManager.REQUEST_PROPERTY_CONTENT_TYPE, "text/html"); connection.setRequestHeaders(headers); connection.setConnectionTimeout(1000); @@ -257,13 +273,12 @@ void testGetRequestDataStringPutBytes () throws Exception { byte[] nullBodyBytes = null; assertEquals("Hello World", new String(connection.getRequestData(url, nullBodyBytes, HttpConnectionType.PUT))); } - - + /** - * Retrieves the data from the end server. Needs an http mock + * Retrieves the data from the end server. Needs an http mock */ @Test - void testGetRequestDataURLMap () throws Exception { + void testGetRequestDataURLMap() throws Exception { headers.put(SMTHttpConnectionManager.REQUEST_PROPERTY_CONTENT_TYPE, "text/html"); connection.setRequestHeaders(headers); connection.setConnectionTimeout(1000); @@ -276,14 +291,15 @@ void testGetRequestDataURLMap () throws Exception { when(mockUrlConn.getOutputStream()).thenReturn(new ByteArrayOutputStream()); doReturn(200).when(mockUrlConn).getResponseCode(); Map nullBodyMap = null; - assertEquals("Hello World", new String(connection.getRequestData(mockUrl, nullBodyMap, HttpConnectionType.PUT))); + assertEquals("Hello World", + new String(connection.getRequestData(mockUrl, nullBodyMap, HttpConnectionType.PUT))); } - + /** - * Retrieves the data from the end server. Needs an http mock + * Retrieves the data from the end server. Needs an http mock */ @Test - void testGetRequestDataURLBytes () throws Exception { + void testGetRequestDataURLBytes() throws Exception { headers.put(SMTHttpConnectionManager.REQUEST_PROPERTY_CONTENT_TYPE, "text/html"); connection.setRequestHeaders(headers); connection.setConnectionTimeout(1000); @@ -296,14 +312,15 @@ void testGetRequestDataURLBytes () throws Exception { when(mockUrlConn.getOutputStream()).thenReturn(new ByteArrayOutputStream()); doReturn(200).when(mockUrlConn).getResponseCode(); byte[] nullBodyBytes = null; - assertEquals("Hello World", new String(connection.getRequestData(mockUrl, nullBodyBytes, HttpConnectionType.PUT))); + assertEquals("Hello World", + new String(connection.getRequestData(mockUrl, nullBodyBytes, HttpConnectionType.PUT))); } - + /** - * Retrieves the data from the end server. Needs an http mock + * Retrieves the data from the end server. Needs an http mock */ @Test - void testGetRequestDataURLNullMap () throws Exception { + void testGetRequestDataURLNullMap() throws Exception { headers.put(SMTHttpConnectionManager.REQUEST_PROPERTY_CONTENT_TYPE, "text/html"); connection.setRequestHeaders(headers); connection.setConnectionTimeout(1000); @@ -318,12 +335,12 @@ void testGetRequestDataURLNullMap () throws Exception { Map nullBodyMap = null; assertEquals("Hello World", new String(connection.getRequestData(mockUrl, nullBodyMap, null))); } - + /** - * Retrieves the data from the end server. Needs an http mock + * Retrieves the data from the end server. Needs an http mock */ @Test - void testGetRequestDataURLNullBytes () throws Exception { + void testGetRequestDataURLNullBytes() throws Exception { headers.put(SMTHttpConnectionManager.REQUEST_PROPERTY_CONTENT_TYPE, "text/html"); connection.setRequestHeaders(headers); connection.setConnectionTimeout(1000); @@ -338,12 +355,12 @@ void testGetRequestDataURLNullBytes () throws Exception { byte[] nullBodyBytes = null; assertEquals("Hello World", new String(connection.getRequestData(mockUrl, nullBodyBytes, null))); } - + /** - * Retrieves the data from the end server. Needs an http mock + * Retrieves the data from the end server. Needs an http mock */ @Test - void testGetRequestDataStringPutNoHeaderMap () throws Exception { + void testGetRequestDataStringPutNoHeaderMap() throws Exception { connection.setConnectionTimeout(1000); connection.setUseCookieHandler(true); connection = Mockito.spy(connection); @@ -359,12 +376,12 @@ void testGetRequestDataStringPutNoHeaderMap () throws Exception { Map nullBodyMap = null; assertEquals("Hello World", new String(connection.getRequestData(url, nullBodyMap, HttpConnectionType.PUT))); } - + /** - * Retrieves the data from the end server. Needs an http mock + * Retrieves the data from the end server. Needs an http mock */ @Test - void testGetRequestDataStringPutNoHeaderBytes () throws Exception { + void testGetRequestDataStringPutNoHeaderBytes() throws Exception { connection.setConnectionTimeout(1000); connection.setUseCookieHandler(true); connection = Mockito.spy(connection); @@ -394,6 +411,7 @@ void testConvertPostData() { /** * Tests the ssl socket information timeout metadata + * * @throws Exception */ @Test @@ -404,6 +422,7 @@ void testSetSslSocketFactory() { /** * Tests the connection timeout metadata + * * @throws Exception */ @Test @@ -414,6 +433,7 @@ void testGetConnectionTimeout() throws Exception { /** * Tests the connection meta data around following redirects + * * @throws Exception */ @Test @@ -424,6 +444,7 @@ void testIsFollowRedirects() throws Exception { /** * Tests the connection request headers + * * @throws Exception */ @Test @@ -431,11 +452,11 @@ void testGetRequestHeaders() throws Exception { Map nullHeaders = null; connection.setRequestHeaders(nullHeaders); assertEquals(0, connection.getRequestHeaders().size()); - + connection.setRequestHeaders(headers); assertEquals(3, connection.getRequestHeaders().size()); } - + /** * * @throws Exception @@ -445,28 +466,29 @@ void testAddRequestHeader() throws Exception { Map nullHeaders = null; String testKey = "testKey"; String testValue = "testValue"; - + connection.setRequestHeaders(nullHeaders); int headerSize = connection.getRequestHeaders().size(); assertFalse(connection.getRequestHeaders().containsKey(testKey)); assertFalse(connection.getRequestHeaders().containsValue(testValue)); connection.addRequestHeader(testKey, testValue); - assertEquals(headerSize+1, connection.getRequestHeaders().size()); + assertEquals(headerSize + 1, connection.getRequestHeaders().size()); assertTrue(connection.getRequestHeaders().containsKey(testKey)); assertTrue(connection.getRequestHeaders().containsValue(testValue)); - + connection.setRequestHeaders(headers); headerSize = connection.getRequestHeaders().size(); assertFalse(connection.getRequestHeaders().containsKey(testKey)); assertFalse(connection.getRequestHeaders().containsValue(testValue)); connection.addRequestHeader(testKey, testValue); - assertEquals(headerSize+1, connection.getRequestHeaders().size()); + assertEquals(headerSize + 1, connection.getRequestHeaders().size()); assertTrue(connection.getRequestHeaders().containsKey(testKey)); assertTrue(connection.getRequestHeaders().containsValue(testValue)); } /** * Test the response code from the request + * * @throws Exception */ @Test @@ -482,40 +504,42 @@ void testGetResponseCode() throws Exception { void testGetCookies() throws Exception { connection.setCookies(cookies); assertEquals(3, connection.getCookies().size()); - + // Loop the original cookies and make sure they are all returned - for(String s: cookies.values()) { + for (String s : cookies.values()) { assertTrue(connection.getCookies().values().contains(s)); } - + cookies = null; connection.setCookies(cookies); assertEquals(0, connection.getCookies().size()); - + HttpURLConnection cConn = null; connection.assignCookies(cConn); } /** * Gets the values in the headerMap + * * @throws Exception */ @Test void testGetHeaderMap() throws Exception { connection.setHeaderMap(cookies); assertEquals(3, connection.getHeaderMap().size()); - + // Loop the original cookies and make sure they are all returned - for(String s: cookies.values()) { + for (String s : cookies.values()) { assertTrue(connection.getHeaderMap().values().contains(s)); } - + connection.setHeaderMap(null); assertEquals(0, connection.getHeaderMap().size()); } /** * Assigns and gets the maximum number of redirects to follow on a request + * * @throws Exception */ @Test @@ -526,6 +550,7 @@ void testGetRedirectLimit() throws Exception { /** * tests getting and setting the cookie handler + * * @throws Exception */ @Test @@ -535,9 +560,9 @@ void testIsUseCookieHandler() throws Exception { } /** - * Tests the get connection stream method. This method performs redirects as - * needed when response code is 30X. - * actions and other + * Tests the get connection stream method. This method performs redirects as + * needed when response code is 30X. actions and other + * * @throws Exception */ @Test @@ -548,7 +573,7 @@ void testGetConnectionStream() throws Exception { URL mockUrl = mock(URL.class, Mockito.withSettings().useConstructor("http://www.siliconmtn.com")); mockUrlConn = mock(HttpURLConnection.class); when(mockUrlConn.getHeaderField("Location")).thenReturn("http://www.google.com"); - + InputStream mis = IOUtils.toInputStream("Hello World", "UTF-8"); when(mockUrl.openConnection()).thenReturn(mockUrlConn); when(mockUrlConn.getInputStream()).thenReturn(mis); @@ -556,16 +581,15 @@ void testGetConnectionStream() throws Exception { doReturn(HttpURLConnection.HTTP_MOVED_PERM).when(mockUrlConn).getResponseCode(); assertTrue(connection.getConnectionStream(mockUrl, null, HttpConnectionType.GET) instanceof InputStream); - doReturn(HttpURLConnection.HTTP_MOVED_PERM).when(mockUrlConn).getResponseCode(); when(mockUrlConn.getHeaderField("Location")).thenReturn(null); assertTrue(connection.getConnectionStream(mockUrl, null, HttpConnectionType.GET) instanceof InputStream); } - + /** - * Tests the get connection stream method. This method performs redirects as - * needed when response code is 30X. - * actions and other + * Tests the get connection stream method. This method performs redirects as + * needed when response code is 30X. actions and other + * * @throws Exception */ @Test @@ -576,7 +600,7 @@ void testGetConnectionStreamRedirNull() throws Exception { URL mockUrl = mock(URL.class, Mockito.withSettings().useConstructor("http://www.siliconmtn.com")); mockUrlConn = mock(HttpURLConnection.class); when(mockUrlConn.getHeaderField("Location")).thenReturn(null); - + InputStream mis = IOUtils.toInputStream("Hello World", "UTF-8"); when(mockUrl.openConnection()).thenReturn(mockUrlConn); when(mockUrlConn.getInputStream()).thenReturn(mis); @@ -584,11 +608,11 @@ void testGetConnectionStreamRedirNull() throws Exception { doReturn(HttpURLConnection.HTTP_MOVED_TEMP).when(mockUrlConn).getResponseCode(); assertTrue(connection.getConnectionStream(mockUrl, null, HttpConnectionType.GET) instanceof InputStream); } - + /** - * Tests the get connection stream method. This method performs redirects as - * needed when response code is 30X. - * actions and other + * Tests the get connection stream method. This method performs redirects as + * needed when response code is 30X. actions and other + * * @throws Exception */ @Test @@ -599,14 +623,14 @@ void testGetConnectionStream404() throws Exception { URL mockUrl = mock(URL.class, Mockito.withSettings().useConstructor("http://www.siliconmtn.com")); mockUrlConn = mock(HttpURLConnection.class); when(mockUrlConn.getHeaderField("Location")).thenReturn(null); - + InputStream mis = IOUtils.toInputStream("Hello World", "UTF-8"); when(mockUrl.openConnection()).thenReturn(mockUrlConn); when(mockUrlConn.getInputStream()).thenReturn(mis); when(mockUrlConn.getErrorStream()).thenReturn(mis); doReturn(404).when(mockUrlConn).getResponseCode(); assertTrue(connection.getConnectionStream(mockUrl, null, HttpConnectionType.GET) instanceof InputStream); - + doReturn(100).when(mockUrlConn).getResponseCode(); assertTrue(connection.getConnectionStream(mockUrl, null, HttpConnectionType.GET) instanceof InputStream); } @@ -621,9 +645,9 @@ void testGetConnectionStreamSSL() throws Exception { when(mockUrlConn.getInputStream()).thenReturn(IOUtils.toInputStream("Hello World", "UTF-8")); when(mockUrlConn.getErrorStream()).thenReturn(IOUtils.toInputStream("Hello World", "UTF-8")); assertTrue(connection.getConnectionStream(mockUrl, null, HttpConnectionType.GET) instanceof InputStream); - + // Test with an SSL Factory - connection.setSslSocketFactory((SSLSocketFactory)SSLSocketFactory.getDefault()); + connection.setSslSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()); mockUrl = mock(URL.class, Mockito.withSettings().useConstructor("https://www.siliconmtn.com")); when(mockUrl.getProtocol()).thenReturn("https"); mockUrlConn = mock(HttpsURLConnection.class); @@ -634,29 +658,31 @@ void testGetConnectionStreamSSL() throws Exception { } /** - * Tests the create URL Method. Validates url is created and exception is - * thrown when null data is presented + * Tests the create URL Method. Validates url is created and exception is thrown + * when null data is presented + * * @throws Exception */ @Test void testCreateURL() throws Exception { assertThrows(IOException.class, () -> connection.createURL(null)); - assertEquals(-1, connection.createURL(sUrl).getPort()); - assertEquals("www.siliconmtn.com", connection.createURL(sUrl).getHost()); - assertEquals("www.siliconmtn.com", connection.createURL("www.siliconmtn.com").getHost()); + assertEquals(-1, connection.createURL(sUrl).getPort()); + assertEquals("www.siliconmtn.com", connection.createURL(sUrl).getHost()); + assertEquals("www.siliconmtn.com", connection.createURL("www.siliconmtn.com").getHost()); - connection.setSslSocketFactory((SSLSocketFactory)SSLSocketFactory.getDefault()); - assertEquals("www.siliconmtn.com", connection.createURL("www.siliconmtn.com").getHost()); + connection.setSslSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()); + assertEquals("www.siliconmtn.com", connection.createURL("www.siliconmtn.com").getHost()); } - + /** * tests the storage of cookies capabilities + * * @throws Exception */ @Test void testStoreCookies() throws Exception { URL myUrl = new URL(sUrl); - mockUrlConn = (HttpURLConnection)myUrl.openConnection(); + mockUrlConn = (HttpURLConnection) myUrl.openConnection(); mockUrlConn = Mockito.spy(mockUrlConn); Mockito.doReturn("JSESSION_ID=12345678").when(mockUrlConn).getHeaderField(0); Mockito.doReturn("AWSALB=AWS_COOKIE").when(mockUrlConn).getHeaderField(1); @@ -674,20 +700,21 @@ void testStoreCookies() throws Exception { } /** - * Tests the assignment of cookies to the url connection class. Utilizes a + * Tests the assignment of cookies to the url connection class. Utilizes a * Mockito Mock class to perform this action + * * @throws Exception */ @Test void testAssignCookiesHttpURLConnection() throws Exception { URL myUrl = new URL(url); - mockUrlConn = (HttpURLConnection)myUrl.openConnection(); + mockUrlConn = (HttpURLConnection) myUrl.openConnection(); connection.assignCookies(mockUrlConn); assertNull(mockUrlConn.getRequestProperty("Cookie")); - + connection.setCookies(cookies); connection.assignCookies(null); - + connection.assignCookies(mockUrlConn); assertTrue(mockUrlConn.getRequestProperty("Cookie").contains("JSESSION_ID")); assertTrue(mockUrlConn.getRequestProperty("Cookie").contains("AWSALB")); @@ -695,19 +722,107 @@ void testAssignCookiesHttpURLConnection() throws Exception { } /** - * Tests the assignment of request headers to the url connection class. Utilizes a - * Mockito Mock class to perform this action + * Tests the assignment of request headers to the url connection class. Utilizes + * a Mockito Mock class to perform this action + * * @throws Exception */ @Test void testSetRequestHeadersHttpURLConnection() throws Exception { connection.setRequestHeaders(headers); URL myUrl = new URL(sUrl); - mockUrlConn = (HttpURLConnection)myUrl.openConnection(); + mockUrlConn = (HttpURLConnection) myUrl.openConnection(); connection.setRequestHeaders(mockUrlConn); - + assertTrue(mockUrlConn.getRequestProperties().toString().contains("Referer")); assertTrue(mockUrlConn.getRequestProperties().toString().contains("User-Agent")); assertFalse(mockUrlConn.getRequestProperties().toString().contains("Host")); } + + @Test + void buildUri() { + String endpoint = "https://test.com"; + String url = "/test/{sessionId}"; + + assertThrows(IllegalArgumentException.class, () -> connection.buildUri(null, null)); + assertThrows(IllegalArgumentException.class, () -> connection.buildUri("", null)); + assertThrows(IllegalArgumentException.class, () -> connection.buildUri(endpoint, null)); + assertThrows(IllegalArgumentException.class, () -> connection.buildUri(endpoint, "")); + + String uri = assertDoesNotThrow(() -> connection.buildUri(endpoint, url)); + assertEquals(endpoint + url, uri); + } + + @Test + void buildUriWithParams() { + UUID sessionId = UUID.randomUUID(); + String endpoint = "https://test.com"; + String url = "/test/{sessionId}"; + + assertThrows(IllegalArgumentException.class, () -> connection.buildUriWithParams(null, null, null)); + assertThrows(IllegalArgumentException.class, () -> connection.buildUriWithParams("", null, null)); + assertThrows(IllegalArgumentException.class, () -> connection.buildUriWithParams(endpoint, null, null)); + assertThrows(IllegalArgumentException.class, () -> connection.buildUriWithParams(endpoint, "", null)); + + String uri = assertDoesNotThrow(() -> connection.buildUriWithParams(endpoint, url, List.of(sessionId))); + assertEquals((endpoint + url).replace("{sessionId}", sessionId.toString()), uri); + } + + @Test + void convertEndpointResponse() throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + mapper.findAndRegisterModules(); + + assertThrows(IllegalArgumentException.class, () -> connection.convertEndpointResponse(null, null, null)); + assertThrows(IllegalArgumentException.class, () -> connection.convertEndpointResponse(new byte[0], null, null)); + assertThrows(IllegalArgumentException.class, + () -> connection.convertEndpointResponse("test".getBytes(), null, null)); + + assertThrows(IllegalArgumentException.class, + () -> connection.convertEndpointResponse("test".getBytes(), String.class, null)); + byte[] data1 = assertDoesNotThrow(() -> mapper.writeValueAsBytes( + new EndpointResponse(HttpStatus.BAD_GATEWAY, new InvalidDataException("There was a problem")))); + assertThrows(IOException.class, () -> connection.convertEndpointResponse(data1, String.class, mapper)); + + byte[] data2 = assertDoesNotThrow(() -> mapper.writeValueAsBytes(new EndpointResponse("test"))); + assertThrows(Exception.class, () -> connection.convertEndpointResponse(data2, GenericVO.class, mapper)); + + byte[] data3 = assertDoesNotThrow(() -> mapper.writeValueAsBytes(new EndpointResponse(new GenericVO()))); + assertDoesNotThrow(() -> connection.convertEndpointResponse(data3, GenericVO.class, mapper)); + } + + @Test + void convertEndpointResponseList() throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + mapper.findAndRegisterModules(); + + assertThrows(IllegalArgumentException.class, () -> connection.convertEndpointResponseList(null, null, null)); + assertThrows(IllegalArgumentException.class, + () -> connection.convertEndpointResponseList(new byte[0], null, null)); + assertThrows(IllegalArgumentException.class, + () -> connection.convertEndpointResponseList("test".getBytes(), null, null)); + + assertThrows(IllegalArgumentException.class, + () -> connection.convertEndpointResponseList("test".getBytes(), new TypeReference>() { + }, null)); + byte[] data1 = assertDoesNotThrow(() -> mapper.writeValueAsBytes( + new EndpointResponse(HttpStatus.BAD_GATEWAY, new InvalidDataException("There was a problem")))); + assertThrows(IOException.class, + () -> connection.convertEndpointResponseList(data1, new TypeReference>() { + }, mapper)); + + byte[] data2 = assertDoesNotThrow(() -> mapper.writeValueAsBytes(new EndpointResponse("test"))); + assertThrows(Exception.class, + () -> connection.convertEndpointResponseList(data2, new TypeReference>() { + }, mapper)); + + List srcData = new ArrayList<>(); + srcData.add(new GenericVO("something", "todo")); + srcData.add(new GenericVO("hello", "world")); + byte[] data3 = assertDoesNotThrow(() -> mapper.writeValueAsBytes(new EndpointResponse(srcData))); + List data = assertDoesNotThrow( + () -> connection.convertEndpointResponseList(data3, new TypeReference>() { + }, mapper)); + assertEquals(srcData.size(), data.size()); + } }