Fix catalog search as per CDS constraints 58/2458/3
authorSerban Jora <sj2381@att.com>
Tue, 31 Jul 2018 19:06:47 +0000 (15:06 -0400)
committerSerban Jora <sj2381@att.com>
Tue, 31 Jul 2018 19:54:24 +0000 (15:54 -0400)
Change-Id: Ic3275d73b6541f09a9e0bc761c568cfc2acc80cf
Signed-off-by: Serban Jora <sj2381@att.com>
36 files changed:
docs/release-notes.rst
gateway/pom.xml
gateway/src/main/java/org/acumos/federation/gateway/cds/Solution.java
gateway/src/main/java/org/acumos/federation/gateway/cds/SolutionRevision.java
gateway/src/main/java/org/acumos/federation/gateway/controller/CatalogController.java
gateway/src/main/java/org/acumos/federation/gateway/security/Role.java
gateway/src/main/java/org/acumos/federation/gateway/service/PeerService.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/AbstractServiceImpl.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/ArtifactServiceImpl.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/ArtifactServiceLocalImpl.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/CatalogServiceImpl.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/CatalogServiceLocalImpl.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/PeerServiceImpl.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/PeerServiceLocalImpl.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/PeerSubscriptionServiceImpl.java
gateway/src/main/java/org/acumos/federation/gateway/task/PeerSubscriptionTaskScheduler.java
gateway/src/test/java/org/acumos/federation/gateway/test/AuthorizationTest.java
gateway/src/test/java/org/acumos/federation/gateway/test/CatalogServiceTest.java [new file with mode: 0644]
gateway/src/test/java/org/acumos/federation/gateway/test/ControllerTest.java
gateway/src/test/java/org/acumos/federation/gateway/test/MockResponse.java [new file with mode: 0644]
gateway/src/test/java/org/acumos/federation/gateway/test/PeerServiceTest.java [new file with mode: 0644]
gateway/src/test/java/org/acumos/federation/gateway/test/ServiceTest.java [new file with mode: 0644]
gateway/src/test/resources/mockCDSDateSolutionsResponsePage0.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSDateSolutionsResponsePage1.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSErrorResponse.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSNoEntryWithIDResponse.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSPeerCreateResponse.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSPeerSearchAllResponse.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSPeerSearchResponse.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSPeerSearchSelfResponse.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSPeerUpdateResponse.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSPortalSolutionsResponse.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSSearchEmptyResponse.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSSolutionResponse.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSSolutionRevisionArtifactsResponse.json [new file with mode: 0644]
gateway/src/test/resources/mockCDSSolutionRevisionsResponse.json [new file with mode: 0644]

index fee0c28..1981fc8 100644 (file)
@@ -22,6 +22,14 @@ Federated Gateway Release Notes
 
 The Federated Gateway server is available as a Docker image in a Docker registry.
 
+Version 1.15.1, 2018-07-31
+-------------------------
+
+* Fixes catalog solution lookup strategy due to used criteria moving to other entities (solution -> revision)
+* Fixes some Sonar complaints
+* Adds more unit tests for CDS based service implementations
+* Align version numbers with CDS
+
 Version 1.1.5, 2018-07-12
 -------------------------
 
index 46f121c..705efe8 100644 (file)
@@ -24,7 +24,7 @@ limitations under the License.
        <modelVersion>4.0.0</modelVersion>
        <groupId>org.acumos.federation</groupId>
        <artifactId>gateway</artifactId>
-       <version>1.1.5-SNAPSHOT</version>
+       <version>1.15.1-SNAPSHOT</version>
        <name>Federation Gateway</name>
        <description>Federated Acumos Interface for inter-acumos and ONAP communication</description>
 
@@ -173,6 +173,12 @@ limitations under the License.
                        <artifactId>commons-lang3</artifactId>
                        <version>3.6</version>
                </dependency>
+               <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
+               <dependency>
+                       <groupId>commons-beanutils</groupId>
+                       <artifactId>commons-beanutils</artifactId>
+                       <version>1.9.3</version>
+               </dependency>
                <dependency>
                        <groupId>com.github.docker-java</groupId>
                        <artifactId>docker-java</artifactId>
index 2259709..8bf0a4e 100644 (file)
@@ -38,6 +38,8 @@ public class Solution extends MLPSolution {
        public static interface Fields {
                public static final String solutionId = "solutionId";
                public static final String name = "name";
+               public static final String description = "description";
+               public static final String tags = "tags";
                public static final String active = "active";
                public static final String modelTypeCode = "modelTypeCode";
                public static final String toolkitTypeCode = "toolkitTypeCode";
index 7b4a883..725fdaa 100644 (file)
@@ -30,6 +30,11 @@ import org.acumos.cds.domain.MLPArtifact;
  */
 public class SolutionRevision extends MLPSolutionRevision {
 
+       public static interface Fields {
+               public static final String accessTypeCode = "accessTypeCode";
+               public static final String validationStatusCode = "validationStatusCode";
+       };
+
        private List<? extends MLPArtifact>             artifacts;
 
        public SolutionRevision() {
index 6f77083..2d3e54c 100644 (file)
@@ -27,6 +27,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Callable;
 
+import java.lang.invoke.MethodHandles;
+
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -70,7 +72,7 @@ import com.github.dockerjava.api.model.Identifier;
 @RequestMapping(API.Roots.FEDERATION)
 public class CatalogController extends AbstractController {
 
-       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(CatalogController.class.getName());
+       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(MethodHandles.lookup().lookupClass());
 
        @Autowired
        private CatalogService catalogService;
@@ -198,7 +200,7 @@ public class CatalogController extends AbstractController {
                                                                                                                        .build();
                                theHttpResponse.setStatus(HttpServletResponse.SC_OK);
                                log.debug(EELFLoggerDelegate.debugLogger, "getSolutionsRevisions for solution {} provided {} revisions",
-                                               theSolutionId, solutionRevisions == null ? 0 : solutionRevisions.size());
+                                               theSolutionId, solutionRevisions.size());
                        }
                }
                catch (Exception x) {
@@ -305,7 +307,7 @@ public class CatalogController extends AbstractController {
                                                                                                                .build();
                                theHttpResponse.setStatus(HttpServletResponse.SC_OK);
                                log.debug(EELFLoggerDelegate.debugLogger, "getSolutionRevisionArtifacts provided {} artifacts",
-                                                       solutionRevisionArtifacts == null ? 0 : solutionRevisionArtifacts.size());
+                                                       solutionRevisionArtifacts.size());
                        }
                } 
                catch (Exception x) {
index 7776c87..90400c0 100644 (file)
@@ -40,13 +40,15 @@ public enum Role {
         * Common active peer, grants generic solution catalog access
         */
        PEER(Collections.unmodifiableList(Arrays.asList(Priviledge.CATALOG_ACCESS,
-                                                                                                                                                                                                       Priviledge.PING_ACCESS))),
+                                                                                                                                                                                                       Priviledge.PING_ACCESS,
+                                                                                                                                                                                                       Priviledge.REGISTRATION_ACCESS))),
        /**
         * Enhanced peer, gains (some lovel of) read access to the local peer list
         */
-       PARTNER(Collections.unmodifiableList(Arrays.asList(Priviledge.CATALOG_ACCESS,
-                                                                                                                                                                                                                Priviledge.PEERS_ACCESS,
-                                                                                                                                                                                                                Priviledge.PING_ACCESS))),
+       PARTNER(Collections.unmodifiableList(Arrays.asList(Priviledge.PEERS_ACCESS,
+                                                                                                                                                                                                                Priviledge.CATALOG_ACCESS,
+                                                                                                                                                                                                                Priviledge.PING_ACCESS,
+                                                                                                                                                                                                                Priviledge.REGISTRATION_ACCESS))),
        /**
         * The Acumos instance this gateway is serving, including local calls and calls
         * received through the gateways' private interface from other components,
index 173437a..d853359 100644 (file)
@@ -23,6 +23,7 @@ package org.acumos.federation.gateway.service;
 import java.util.List;
 
 import org.acumos.cds.domain.MLPPeer;
+import org.acumos.federation.gateway.cds.PeerStatus;
 
 /**
  * Defines the interface of a service providing local peer information.
@@ -120,6 +121,40 @@ public interface PeerService {
         */
        public void registerPeer(MLPPeer thePeer) throws ServiceException;
 
+       /**
+        * Asserts the registration request pre-conditions.
+        * Returns if peer registration can continue.
+        *
+        * @param thePeer
+        *            new peer info to be registered
+        * @throws ServiceException
+        *             In order to signal an invalid peer status at the time of a registration request
+        */
+       public default void assertPeerRegistration(MLPPeer thePeer) throws ServiceException {
+
+               if (thePeer == null)
+                       return;
+                       
+               PeerStatus status = PeerStatus.forCode(thePeer.getStatusCode());
+               if (null == status) {
+                       throw new ServiceException("Invalid peer status found: " + thePeer.getStatusCode());
+               }
+
+               if (status == PeerStatus.Requested) {
+                       throw new ServiceException("Peer registration request is pending");
+               }
+               else if (status == PeerStatus.Declined) {
+                       throw new ServiceException("Peer registration request was declined");
+               }
+               else if (status == PeerStatus.Renounced) {
+                       throw new ServiceException("Peer unregistration request is pending");
+               }
+               else { //if (status == PeerStatus.Active || status == PeerStatus.Inactive) {
+                       throw new ServiceException("Peer already exists: " + thePeer);
+               }
+       }
+       
+
        /**
         * Optional operation allowing the gateway to update a peer and mark it for
         * removal as part of a in-band peer handshake mechanism.
@@ -131,6 +166,36 @@ public interface PeerService {
         */
        public void unregisterPeer(MLPPeer thePeer) throws ServiceException;
 
+       /**
+        * Asserts the unregistration request pre-conditions.
+        * Returns if peer unregistration can continue.
+        *
+        * @param thePeer
+        *            Peer info.
+        * @throws ServiceException
+        *             In order to signal an invalid peer status at the time of a unregistration request
+        */
+       public default void assertPeerUnregistration(MLPPeer thePeer) throws ServiceException {
+       
+               if (thePeer == null)
+                       throw new ServiceException("No such peer found: " + thePeer.getSubjectName());
+
+               PeerStatus status = PeerStatus.forCode(thePeer.getStatusCode());
+               if (null == status) {
+                       throw new ServiceException("Invalid peer status found: " + thePeer.getStatusCode());
+               }
+
+               if (status == PeerStatus.Requested) {
+                       throw new ServiceException("Peer registration request is pending");
+               }
+               else if (status == PeerStatus.Declined) {
+                       throw new ServiceException("Peer registration request was declined");
+               }
+               else if (status == PeerStatus.Renounced) {
+                       throw new ServiceException("Peer unregistration request is pending");
+               }
+       }
+
        /**
         * Provide a context for self service calls, ie calls made on the behalf of the gateway itself.
         */
index c9bae86..a8a32dc 100644 (file)
@@ -42,7 +42,23 @@ public abstract class AbstractServiceImpl {
                return clients.getCDSClient();
        }
 
+       public ICommonDataServiceRestClient getClient(ServiceContext theContext) {
+               ICommonDataServiceRestClient client = (ICommonDataServiceRestClient)theContext.getAttribute(Attributes.cdsClient);
+               if (client == null)
+                       client = getClient();
+
+               return client;
+       }
+
        public ServiceContext selfService() {
                return ServiceContext.forPeer((Peer)appCtx.getBean("self"));            
        }
+
+       /**
+        * Define context attributes used by derived implementations.
+        */
+       public static interface Attributes {
+
+               public static final String cdsClient = "cdsClient";
+       }
 }
index 93d5318..45f172b 100644 (file)
@@ -35,6 +35,8 @@ import java.util.Map;
 import java.util.Date;
 import java.util.stream.Collectors;
 
+import java.lang.invoke.MethodHandles;
+
 import javax.annotation.PostConstruct;
 
 import org.apache.commons.io.FileUtils;
@@ -77,7 +79,7 @@ import org.springframework.context.annotation.Conditional;
 public class ArtifactServiceImpl extends AbstractServiceImpl
                                                                                                                                        implements ArtifactService {
 
-       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(ArtifactServiceImpl.class.getName());
+       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(MethodHandles.lookup().lookupClass());
 
        /**
         * @return a resource containing the content or null if the artifact has no content
@@ -97,10 +99,11 @@ public class ArtifactServiceImpl extends AbstractServiceImpl
                                //pull followed by save
                                DockerClient docker = this.clients.getDockerClient();
 
-                               PullImageResultCallback pullResult = new PullImageResultCallback(); 
-                               docker.pullImageCmd(theArtifact.getUri())
-                                                       .exec(pullResult);
-                               pullResult.awaitCompletion();
+                               try (PullImageResultCallback pullResult = new PullImageResultCallback()) {
+                                       docker.pullImageCmd(theArtifact.getUri())
+                                                               .exec(pullResult);
+                                       pullResult.awaitCompletion();
+                               }
 
                                return new InputStreamResource(docker.saveImageCmd(theArtifact.getUri()).exec());
                        }
@@ -133,15 +136,15 @@ public class ArtifactServiceImpl extends AbstractServiceImpl
                                                        .exec(); //sync xecution
 
                                // there is an assumption here that the repo info was stripped from the artifact name by the originator
-                               PushImageResultCallback pushResult = new PushImageResultCallback();
                                Identifier imageId =
                                        new Identifier(
                                                new Repository(this.clients.getDockerProperty("registryUrl").toString()),
-                                               theArtifact.getName() /*the tag*/);
-                               docker.pushImageCmd(imageId)
-                                                       .exec(pushResult);
-                               pushResult.awaitCompletion();
-                               
+                                                                                                        theArtifact.getName() /*the tag*/);
+                               try (PushImageResultCallback pushResult = new PushImageResultCallback()) {
+                                       docker.pushImageCmd(imageId)
+                                                               .exec(pushResult);
+                                       pushResult.awaitCompletion();
+                               }       
                                // update artifact with local repo reference. we also update the name and description in order to stay
                                // alligned with on-boarding's unwritten rules
                                theArtifact.setUri(imageId.toString());
index a548c6f..36e252f 100644 (file)
@@ -29,6 +29,8 @@ import java.io.IOException;
 import java.net.URI;
 import java.util.stream.Collectors;
 
+import java.lang.invoke.MethodHandles;
+
 import javax.annotation.PostConstruct;
 
 import org.apache.commons.io.FileUtils;
@@ -58,7 +60,7 @@ import org.apache.commons.io.FileUtils;
 public class ArtifactServiceLocalImpl extends AbstractServiceImpl
                                                                                                                                        implements ArtifactService {
 
-       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(ArtifactServiceLocalImpl.class.getName());
+       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(MethodHandles.lookup().lookupClass());
 
        /**
         * @return a resource containing the content or null if the artifact has no content
index 3cf6022..158fcdf 100644 (file)
@@ -33,8 +33,11 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Date;
+import java.util.Collections;
 import java.util.stream.Collectors;
 
+import java.lang.invoke.MethodHandles;
+
 import javax.annotation.PostConstruct;
 
 import org.apache.commons.io.FileUtils;
@@ -68,6 +71,8 @@ import org.acumos.federation.gateway.cds.Solution;
 import org.acumos.federation.gateway.cds.SolutionRevision;
 import org.acumos.federation.gateway.cds.Artifact;
 
+import org.apache.commons.beanutils.PropertyUtils;
+
 /**
  * CDS based implementation of the CatalogService.
  *
@@ -76,23 +81,29 @@ import org.acumos.federation.gateway.cds.Artifact;
 public class CatalogServiceImpl extends AbstractServiceImpl
                                                                                                                                implements CatalogService {
 
-       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(CatalogServiceImpl.class.getName());
+       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(MethodHandles.lookup().lookupClass());
 
        @Autowired
        private Environment env;
 
-       private Map<String, Object> baseSelector;
+       private Map<String, Object> baseSolutionSelector,
+       /*private List<Predicate<MLPSolutionRevision>>*/        baseSolutionRevisionSelector;
 
        @PostConstruct
        public void initService() {
-               baseSelector = new HashMap<String, Object>();
-
+               baseSolutionSelector = new HashMap<String, Object>();
                // Fetch all active solutions
-               baseSelector.put(Solution.Fields.active, true);
+               baseSolutionSelector.put(Solution.Fields.active, true);
                // Fetch allowed only for Public models
-               baseSelector.put(Solution.Fields.accessTypeCode, AccessTypeCode.PB.toString());
+               baseSolutionSelector.put(Solution.Fields.accessTypeCode, AccessTypeCode.PB.toString());
+               // Validation status should be passed locally
+               baseSolutionSelector.put(Solution.Fields.validationStatusCode, ValidationStatusCode.PS.toString());
+
+               baseSolutionRevisionSelector = new HashMap<String, Object>();
+               // Fetch allowed only for Public revisions
+               baseSolutionRevisionSelector.put(SolutionRevision.Fields.accessTypeCode, AccessTypeCode.PB.toString());
                // Validation status should be passed locally
-               baseSelector.put(Solution.Fields.validationStatusCode, ValidationStatusCode.PS.toString());
+               baseSolutionRevisionSelector.put(SolutionRevision.Fields.validationStatusCode, ValidationStatusCode.PS.toString());
        }
 
        @Override
@@ -106,44 +117,70 @@ public class CatalogServiceImpl extends AbstractServiceImpl
                        selector.putAll(theSelector);
                //it is essential that this gets done at the end as to force all baseSelector criteria (otherwise a submitted accessTypeCode
                //could overwrite the basic one end expose non public solutions ..).
-               selector.putAll(this.baseSelector);
+               selector.putAll(this.baseSolutionSelector);
                log.debug(EELFLoggerDelegate.debugLogger, "getSolutions with full selector {}", selector);
 
-               RestPageRequest pageRequest = new RestPageRequest(0, 5);
+               RestPageRequest pageRequest = new RestPageRequest(0, 50);
                RestPageResponse<MLPSolution> pageResponse = null;
                List<MLPSolution> solutions = new ArrayList<MLPSolution>(),
                                                                                        pageSolutions = null;
                ICommonDataServiceRestClient cdsClient = getClient();
-
-               do {
-                       log.debug(EELFLoggerDelegate.debugLogger, "getSolutions page {}", pageResponse);
-                       if (selector.containsKey(Solution.Fields.modified)) {
-                               //Use the dedicated api: this is a 'deep' application of the 'modified' criteria as it will look into revisions
-                               //and artifacts for related information modified since.
-                               pageResponse =
-                                       cdsClient.findSolutionsByDate(
-                                               (Boolean)baseSelector.get(Solution.Fields.active),
-                                               new String[] {selector.get(Solution.Fields.accessTypeCode).toString()},
-                                               new String[] {selector.get(Solution.Fields.validationStatusCode).toString()},
-                                               new Date((Long)selector.get(Solution.Fields.modified)),
-                                               pageRequest);
+               try {
+                       do {
+                               log.debug(EELFLoggerDelegate.debugLogger, "getSolutions page {}", pageResponse);
+                               if (selector.containsKey(Solution.Fields.modified)) {
+                                       //Use the dedicated api: this is a 'deep' application of the 'modified' criteria as it will look into revisions
+                                       //and artifacts for related information modified since.
+                                       pageResponse =
+                                               cdsClient.findSolutionsByDate(
+                                                       (Boolean)selector.get(Solution.Fields.active),
+                                                       new String[] {selector.get(Solution.Fields.accessTypeCode).toString()},
+                                                       new String[] {selector.get(Solution.Fields.validationStatusCode).toString()},
+                                                       new Date((Long)selector.get(Solution.Fields.modified)),
+                                                       pageRequest);
                        
-                               //we need to post-process all other selection criteria
-                               pageSolutions = pageResponse.getContent().stream()
-                                                                                                       .filter(solution -> ServiceImpl.isSelectable(solution, theSelector))
-                                                                                                       .collect(Collectors.toList());
+                                       //we need to post-process all other selection criteria
+                                       pageSolutions = pageResponse.getContent().stream()
+                                                                                                               .filter(solution -> ServiceImpl.isSelectable(solution, theSelector))
+                                                                                                               .collect(Collectors.toList());
+                               }
+                               else {
+                                       pageResponse =
+                                               cdsClient.findPortalSolutions(selector.containsKey(Solution.Fields.name) ?
+                                                                                                                                                                               new String[] {selector.get(Solution.Fields.name).toString()} :
+                                                                                                                                                                               null,
+                                                                                                                                                                       selector.containsKey(Solution.Fields.description) ?
+                                                                                                                                                                               new String[] {selector.get(Solution.Fields.description).toString()} :
+                                                                                                                                                                               null,
+                                                                                                                                                                       (Boolean)selector.get(Solution.Fields.active),
+                                                                                                                                                                       null, //owner ids
+                                                                                                                                                                       new String[] {selector.get(Solution.Fields.accessTypeCode).toString()},
+                                                                                                                                                                       selector.containsKey(Solution.Fields.modelTypeCode) ?
+                                                                                                                                                                               new String[] {selector.get(Solution.Fields.modelTypeCode).toString()} :
+                                                                                                                                                                               null,
+                                                                                                                                                                       new String[] {selector.get(Solution.Fields.validationStatusCode).toString()},
+                                                                                                                                                                       selector.containsKey(Solution.Fields.tags) ?
+                                                                                                                                                                               new String[] {selector.get(Solution.Fields.tags).toString()} :
+                                                                                                                                                                               null,
+                                                                                                                                                                       pageRequest);
+                                               //cdsClient.searchSolutions(selector, false, pageRequest);
+                                       pageSolutions = pageResponse.getContent();
+                               }
+                               log.debug(EELFLoggerDelegate.debugLogger, "getSolutions page response {}", pageResponse);
+               
+                               pageRequest.setPage(pageResponse.getNumber() + 1);
+                               solutions.addAll(pageSolutions);
                        }
+                       while (!pageResponse.isLast());
+               }
+               catch (HttpStatusCodeException restx) {
+                       if (Errors.isCDSNotFound(restx))
+                               return Collections.EMPTY_LIST;
                        else {
-                               pageResponse =
-                                       cdsClient.searchSolutions(selector, false, pageRequest);
-                               pageSolutions = pageResponse.getContent();
+                               log.debug(EELFLoggerDelegate.debugLogger, "getSolutions failed {}: {}", restx, restx.getResponseBodyAsString());
+                               throw new ServiceException("Failed to retrieve solutions", restx);
                        }
-                       log.debug(EELFLoggerDelegate.debugLogger, "getSolutions page response {}", pageResponse);
-               
-                       pageRequest.setPage(pageResponse.getNumber() + 1);
-                       solutions.addAll(pageSolutions);
                }
-               while (!pageResponse.isLast());
 
                log.debug(EELFLoggerDelegate.debugLogger, "getSolutions: solutions count {}", solutions.size());
                return solutions;
@@ -156,7 +193,13 @@ public class CatalogServiceImpl extends AbstractServiceImpl
                ICommonDataServiceRestClient cdsClient = getClient();
                try {
                        Solution solution = (Solution)cdsClient.getSolution(theSolutionId);
-                       solution.setRevisions(cdsClient.getSolutionRevisions(theSolutionId));
+                       List<MLPSolutionRevision> revisions = getSolutionRevisions(theSolutionId, theContext.withAttribute(Attributes.cdsClient, cdsClient));
+
+                       //we can expose this solution only if we can expose at least one revision
+                       if (revisions == null || revisions.isEmpty())
+                               return null;
+
+                       solution.setRevisions(revisions);
                        return solution;
                }
                catch (HttpStatusCodeException restx) {
@@ -172,7 +215,20 @@ public class CatalogServiceImpl extends AbstractServiceImpl
 
                log.trace(EELFLoggerDelegate.debugLogger, "getSolutionRevisions");
                try {
-                       return getClient().getSolutionRevisions(theSolutionId);
+                       List<MLPSolutionRevision> revisions = getClient(theContext).getSolutionRevisions(theSolutionId);
+                       //make sure we only expose revisions according to the filter
+                       if (revisions != null) {
+                               revisions = 
+                                       revisions.stream()
+                                                                        .filter(revision -> baseSolutionRevisionSelector.entrySet().stream()
+                                                                                                                                                               .allMatch(s -> { try {
+                                                                                                                                                                                                                                       return PropertyUtils.getProperty(revision, s.getKey()).equals(s.getValue());
+                                                                                                                                                                                                                                } catch (Exception x) { return false; }
+                                                                                                                                                                                                                        })
+                                                                                                       )
+                                                                        .collect(Collectors.toList());
+                       }
+                       return revisions;
                }
                catch (HttpStatusCodeException restx) {
                        if (Errors.isCDSNotFound(restx))
@@ -182,6 +238,7 @@ public class CatalogServiceImpl extends AbstractServiceImpl
                }
        }
 
+
        @Override
        public SolutionRevision getSolutionRevision(String theSolutionId, String theRevisionId,
                        ServiceContext theContext) throws ServiceException {
index e4591f8..3cba007 100644 (file)
@@ -37,6 +37,8 @@ import java.util.Map;
 import java.util.Collections;
 import java.util.stream.Collectors;
 
+import java.lang.invoke.MethodHandles;
+
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 
@@ -84,7 +86,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 @ConfigurationProperties(prefix = "catalogLocal")
 public class CatalogServiceLocalImpl extends AbstractServiceLocalImpl implements CatalogService {
 
-       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(CatalogServiceLocalImpl.class.getName());
+       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(MethodHandles.lookup().lookupClass());
 
        private List<Solution> solutions;
 
index fd47ef4..42e62fa 100644 (file)
@@ -29,6 +29,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.ArrayList;
 
+import java.lang.invoke.MethodHandles;
+
 import org.acumos.federation.gateway.config.EELFLoggerDelegate;
 import org.acumos.federation.gateway.util.MapBuilder;
 import org.acumos.federation.gateway.service.PeerService;
@@ -53,7 +55,7 @@ import org.acumos.cds.transport.RestPageRequest;
 @Service
 public class PeerServiceImpl extends AbstractServiceImpl implements PeerService {
 
-       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(PeerServiceImpl.class.getName());
+       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(MethodHandles.lookup().lookupClass());
 
        /**
         * 
@@ -130,31 +132,11 @@ public class PeerServiceImpl extends AbstractServiceImpl implements PeerService
                RestPageResponse<MLPPeer> response = 
                        cdsClient.searchPeers(new MapBuilder().put("subjectName", subjectName).build(), false, null);
 
-               if (response.getSize() != 0) {
-                       //if (response.getSize() == 1) { //should be the only alternative
-                       MLPPeer peer = response.getContent().get(0);
-                       PeerStatus status = PeerStatus.forCode(peer.getStatusCode());
-                       if (null == status) {
-                               throw new ServiceException("Invalid peer status found: " + peer.getStatusCode());
-                       }
-
-                       if (status == PeerStatus.Requested) {
-                               throw new ServiceException("Peer registration request is pending");
-                       }
-                       else if (status == PeerStatus.Active || status == PeerStatus.Inactive) {
-                               log.info(EELFLoggerDelegate.applicationLogger, "registering an active/inactive peer: " + peer);
-                               return;
-                       }
-                       else if (status == PeerStatus.Declined) {
-                               throw new ServiceException("Peer registration request was declined");
-                       }
-                       else if (status == PeerStatus.Renounced) {
-                               throw new ServiceException("Peer unregistration request is pending");
-                       }
-                       throw new ServiceException("Peer with subjectName '" + subjectName + "' already exists: " + peer);
+               if (response.getNumberOfElements() > 0) {
+                       assertPeerRegistration(response.getContent().get(0));
                }
 
-               log.error(EELFLoggerDelegate.debugLogger, "registerPeer: new peer with subjectName {}, create CDS record",
+               log.info(EELFLoggerDelegate.debugLogger, "registerPeer: new peer with subjectName {}, create CDS record",
                                thePeer.getSubjectName());
                //enforce
                thePeer.setStatusCode(PeerStatus.Requested.code());
@@ -163,7 +145,7 @@ public class PeerServiceImpl extends AbstractServiceImpl implements PeerService
                        cdsClient.createPeer(thePeer);
                }
                catch (Exception x) {
-                       throw new ServiceException("Failed to create peer");
+                       throw new ServiceException("Failed to create peer", x);
                }
        }
 
@@ -179,30 +161,15 @@ public class PeerServiceImpl extends AbstractServiceImpl implements PeerService
                RestPageResponse<MLPPeer> response = 
                        cdsClient.searchPeers(new MapBuilder().put("subjectName", subjectName).build(), false, null);
 
-               if (response.getSize() != 1) {
-                       throw new ServiceException("Search for peer with subjectName '" + subjectName + "' yielded invalid number of items: " + response);
+               if (response.getNumberOfElements() != 1) {
+                       throw new ServiceException("Search for peer with subjectName '" + subjectName + "' yielded invalid number of items: " + response.getNumberOfElements());
                }
 
                MLPPeer peer = response.getContent().get(0);
-               PeerStatus status = PeerStatus.forCode(peer.getStatusCode());
-               if (null == status) {
-                       throw new ServiceException("Invalid peer status found: " + peer.getStatusCode());
-               }
+               assertPeerUnregistration(peer);
 
-               if (status == PeerStatus.Requested) {
-                       throw new ServiceException("Peer registration request is pending");
-                       //can we simply delete the peer ??
-               }
-               else if (status == PeerStatus.Declined) {
-                       throw new ServiceException("Peer registration request was declined");
-                       //can we simply delete the peer ??
-               }
-               else if (status == PeerStatus.Renounced) {
-                       throw new ServiceException("Peer unregistration request is pending");
-               }
                //active/inactive peers moved to renounced
-
-               log.error(EELFLoggerDelegate.debugLogger, "unregisterPeer: peer with subjectName {}, update CDS record",
+               log.info(EELFLoggerDelegate.debugLogger, "unregisterPeer: peer with subjectName {}, update CDS record",
                                thePeer.getSubjectName());
                thePeer.setStatusCode(PeerStatus.Renounced.code());
 
index 5059feb..a010dda 100644 (file)
@@ -37,6 +37,8 @@ import java.util.Date;
 import java.util.Collections;
 import java.util.stream.Collectors;
 
+import java.lang.invoke.MethodHandles;
+
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 
@@ -69,7 +71,7 @@ import org.apache.commons.io.IOUtils;
 @ConfigurationProperties(prefix = "peersLocal")
 public class PeerServiceLocalImpl extends AbstractServiceLocalImpl implements PeerService, PeerSubscriptionService {
 
-       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(PeerServiceLocalImpl.class.getName());
+       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(MethodHandles.lookup().lookupClass());
 
        private List<FLPPeer> peers;
 
@@ -149,15 +151,26 @@ public class PeerServiceLocalImpl extends AbstractServiceLocalImpl implements Pe
 
        /** */
        @Override
-       public void registerPeer(MLPPeer mlpPeer) {
-               log.info(EELFLoggerDelegate.debugLogger, "Registered peer {}", mlpPeer);
-               //this.peers.put(new FLPPeer(mlpPeer));
+       public void registerPeer(MLPPeer thePeer) throws ServiceException {
+               log.info(EELFLoggerDelegate.debugLogger, "Registered peer {}", thePeer);
+               List<MLPPeer> peers = getPeerBySubjectName(thePeer.getSubjectName());
+               if (peers.size() > 0) {
+                       assertPeerRegistration(peers.get(0));
+               }
+               this.peers.add(new FLPPeer(thePeer));
        }
 
        /** */
        @Override
-       public void unregisterPeer(MLPPeer mlpPeer) {
-               throw new UnsupportedOperationException();
+       public void unregisterPeer(MLPPeer thePeer) throws ServiceException {
+               log.info(EELFLoggerDelegate.debugLogger, "Unregistered peer {}", thePeer);
+               List<MLPPeer> peers = getPeerBySubjectName(thePeer.getSubjectName());
+               if (peers.size() > 0) {
+                       assertPeerUnregistration(peers.get(0));
+                       this.peers.remove(peers.get(0));
+               }
+               else
+                       throw new ServiceException("No such peer " + thePeer);
        }
 
        /** */
@@ -204,6 +217,13 @@ public class PeerServiceLocalImpl extends AbstractServiceLocalImpl implements Pe
                @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
                private List<MLPPeerSubscription> subscriptions;
 
+               public FLPPeer() {
+               }
+
+               public FLPPeer(MLPPeer theSource) {
+                       super(theSource);
+               }
+
                // @JsonIgnore
                public List<MLPPeerSubscription> getSubscriptions() {
                        return this.subscriptions;
index 50d5341..e32d731 100644 (file)
  * limitations under the License.
  * ===============LICENSE_END=========================================================
  */
-
-/**
- * 
- */
 package org.acumos.federation.gateway.service.impl;
 
 import java.util.List;
 import java.util.stream.Collectors;
 
+import java.lang.invoke.MethodHandles;
+
 import org.acumos.federation.gateway.config.EELFLoggerDelegate;
 import org.acumos.federation.gateway.service.PeerSubscriptionService;
 import org.acumos.federation.gateway.service.ServiceException;
@@ -41,20 +39,16 @@ import org.acumos.cds.domain.MLPPeerSubscription;
 import org.acumos.cds.transport.RestPageResponse;
 
 /**
- * 
- *
+ * CDS based implementation of PeerSubscriptionService.
  */
 @Service
 public class PeerSubscriptionServiceImpl extends AbstractServiceImpl implements PeerSubscriptionService {
 
-       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(PeerSubscriptionServiceImpl.class.getName());
+       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(MethodHandles.lookup().lookupClass());
 
        @Autowired
        private Environment env;
 
-       /**
-        * 
-        */
        public PeerSubscriptionServiceImpl() {
        }
 
index b1ce7c8..7327d3c 100644 (file)
@@ -25,6 +25,8 @@ import java.util.Map;
 import java.util.Date;
 import java.util.concurrent.ScheduledFuture;
 
+import java.lang.invoke.MethodHandles;
+
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 
@@ -58,7 +60,7 @@ import com.google.common.collect.Table;
 @EnableScheduling
 public class PeerSubscriptionTaskScheduler {
 
-       private final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(getClass().getName());
+       private static final EELFLoggerDelegate log = EELFLoggerDelegate.getLogger(MethodHandles.lookup().lookupClass());
 
        @Autowired
        private Environment env;
@@ -157,6 +159,8 @@ public class PeerSubscriptionTaskScheduler {
                        if (peer.isSelf())
                                continue;
 
+                       log.debug(EELFLoggerDelegate.debugLogger,
+                                               "processing peer {}", peer.getPeerId());
                        // cancel peer tasks for non-active peers
                        if (PeerStatus.Active != PeerStatus.forCode(peer.getStatusCode())) {
                                // cancel all peer sub tasks for this peer
index 72a3332..f0f5d68 100644 (file)
@@ -111,7 +111,7 @@ public class AuthorizationTest {
                }
                
                assertTrue(response != null);
-               assertTrue(response.getStatusCodeValue() == 401);
+               assertTrue("Expected status code 401, got " + response.getStatusCodeValue(), response.getStatusCodeValue() == 401);
        }
 
        @Test
@@ -130,7 +130,7 @@ public class AuthorizationTest {
                }
                
                assertTrue(response != null);
-               assertTrue(response.getStatusCodeValue() == 200);
+               assertTrue("Expected status code 200, got " + response.getStatusCodeValue(), response.getStatusCodeValue() == 200);
                assertTrue(response.getBody().getContent().size() == 1);
        
        }
@@ -151,7 +151,7 @@ public class AuthorizationTest {
                }
                
                assertTrue(response != null);
-               assertTrue(response.getStatusCodeValue() == 202); //401 ??
+               assertTrue("Expected status code 202, got " + response.getStatusCodeValue(), response.getStatusCodeValue() == 202);
        }
 
 
diff --git a/gateway/src/test/java/org/acumos/federation/gateway/test/CatalogServiceTest.java b/gateway/src/test/java/org/acumos/federation/gateway/test/CatalogServiceTest.java
new file mode 100644 (file)
index 0000000..315f5f6
--- /dev/null
@@ -0,0 +1,237 @@
+/*-
+ * ===============LICENSE_START=======================================================
+ * Acumos
+ * ===================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * ===================================================================================
+ * This Acumos software file is distributed by AT&T and Tech Mahindra
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ===============LICENSE_END=========================================================
+ */
+package org.acumos.federation.gateway.test;
+
+import java.io.InputStream;
+import java.io.Closeable;
+import java.io.IOException;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Collections;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.CountDownLatch;
+
+import org.junit.Before;
+import static org.junit.Assert.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat; 
+
+import org.junit.FixMethodOrder;
+import org.junit.runners.MethodSorters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.EventListener;
+import org.springframework.context.annotation.Bean;
+
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.core.io.ClassPathResource;
+
+import static org.mockito.Mockito.*;
+import static org.mockito.Matchers.*;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+import org.mockito.invocation.InvocationOnMock;
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.client.methods.CloseableHttpResponse; 
+import org.apache.http.message.BasicStatusLine;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+
+/* this is not good for unit testing .. */
+import org.acumos.federation.gateway.common.JsonResponse;
+import org.acumos.federation.gateway.event.PeerSubscriptionEvent;
+import org.acumos.federation.gateway.common.Clients;
+import org.acumos.federation.gateway.config.LocalInterfaceConfiguration;
+import org.acumos.federation.gateway.cds.PeerStatus;
+import org.acumos.federation.gateway.security.Peer;
+import org.acumos.federation.gateway.security.Role;
+import org.acumos.federation.gateway.service.CatalogService;
+import org.acumos.federation.gateway.service.ServiceContext;
+import org.acumos.federation.gateway.service.ServiceException;
+import org.acumos.federation.gateway.cds.Solution;
+import org.acumos.federation.gateway.cds.SolutionRevision;
+
+import org.acumos.cds.domain.MLPPeer;
+import org.acumos.cds.domain.MLPPeerSubscription;
+import org.acumos.cds.domain.MLPSolution;
+import org.acumos.cds.domain.MLPSolutionRevision;
+import org.acumos.cds.domain.MLPArtifact;
+import org.acumos.cds.client.ICommonDataServiceRestClient;
+import org.acumos.cds.transport.RestPageRequest;
+import org.acumos.cds.transport.RestPageResponse;
+
+import org.acumos.nexus.client.NexusArtifactClient;
+import org.acumos.nexus.client.data.UploadArtifactInfo;
+
+
+/**
+ */
+
+//@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = org.acumos.federation.gateway.Application.class,
+                                                               webEnvironment = WebEnvironment.RANDOM_PORT,
+                                                               properties = {
+                                                                       "federation.instance=gateway",
+                                                                       "federation.instance.name=test",
+                                                                       "federation.operator=admin",
+                                                                       "federation.ssl.key-store=classpath:acumosa.pkcs12",
+                                                                       "federation.ssl.key-store-password=acumosa",
+                                                                       "federation.ssl.key-store-type=PKCS12",
+                                                                       "federation.ssl.key-password = acumosa",
+                                                                       "federation.ssl.trust-store=classpath:acumosTrustStore.jks",
+                                                                       "federation.ssl.trust-store-password=acumos",
+                                                                       "federation.ssl.client-auth=need",
+                                                                       "local.addr=127.0.0.1",
+                                                                       "local.server.port=9011",
+                                                                       "local.ssl.key-store=classpath:acumosa.pkcs12",
+                                                                       "local.ssl.key-store-password=acumosa",
+                                                                       "local.ssl.key-store-type=PKCS12",
+                                                                       "local.ssl.key-password=acumosa",
+                                                                       "cdms.client.url=http://localhost:8000/ccds",
+                                                                       "cdms.client.username=username",
+                                                                       "cdms.client.password=password"
+                                                                       })
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class CatalogServiceTest extends ServiceTest {
+
+       
+       @Autowired
+       private CatalogService catalog;
+
+       protected void initMockResponses() {
+
+               registerMockResponse("GET /ccds/solution/search/portal?atc=PB&vsc=PS&active=true&page=0&size=50", MockResponse.success("mockCDSPortalSolutionsResponse.json"));
+               registerMockResponse("GET /ccds/solution/search/date?atc=PB&datems=1531747662&vsc=PS&active=true&page=0&size=50", MockResponse.success("mockCDSDateSolutionsResponsePage0.json"));
+               registerMockResponse("GET /ccds/solution/search/date?atc=PB&datems=1531747662&vsc=PS&active=true&page=1&size=50", MockResponse.success("mockCDSDateSolutionsResponsePage1.json"));
+               registerMockResponse("GET /ccds/solution/10101010-1010-1010-1010-101010101010", MockResponse.success("mockCDSSolutionResponse.json"));
+               registerMockResponse("GET /ccds/solution/10101010-1010-1010-1010-101010101010/revision", MockResponse.success("mockCDSSolutionRevisionsResponse.json"));
+               registerMockResponse("GET /ccds/solution/10101010-1010-1010-1010-101010101010/revision/a0a0a0a0-a0a0-a0a0-a0a0-a0a0a0a0a0a0/artifact", MockResponse.success("mockCDSSolutionRevisionArtifactsResponse.json"));
+               registerMockResponse("GET /ccds/solution/f0f0f0f0-f0f0-f0f0-f0f0-f0f0f0f0f0f0", new MockResponse(400, "Error", "mockCDSNoEntryWithIDResponse.json"));
+               registerMockResponse("GET /ccds/solution/f0f0f0f0-f0f0-f0f0-f0f0-f0f0f0f0f0f0/revision", new MockResponse(400, "Error", "mockCDSNoEntryWithIDResponse.json"));
+               registerMockResponse("GET /ccds/solution/f0f0f0f0-f0f0-f0f0-f0f0-f0f0f0f0f0f0/revision/f0f0f0f0-f0f0-f0f0-f0f0-f0f0f0f0f0f0/artifact", new MockResponse(400, "Error", "mockCDSNoEntryWithIDResponse.json"));
+               registerMockResponse("GET /ccds/solution/f1f1f1f1-f1f1-f1f1-f1f1-f1f1f1f1f1f1", new MockResponse(412, "Error", "mockCDSErrorResponse.json"));
+               registerMockResponse("GET /ccds/solution/f1f1f1f1-f1f1-f1f1-f1f1-f1f1f1f1f1f1/revision", new MockResponse(412, "Error", "mockCDSErrorResponse.json"));
+               registerMockResponse("GET /ccds/solution/f1f1f1f1-f1f1-f1f1-f1f1-f1f1f1f1f1f1/revision/f1f1f1f1-f1f1-f1f1-f1f1-f1f1f1f1f1f1/artifact", new MockResponse(412, "Error", "mockCDSErrorResponse.json"));
+               registerMockResponse("GET /ccds/peer/search?isSelf=true&_j=a", MockResponse.success("mockCDSPeerSearchSelfResponse.json"));
+       }
+
+       /**
+        * The gateway behaviour is triggered by the availability of other solutions
+        * in a peer, as provided by the federation client.  
+        */
+       @Test
+       public void testCatalogService() {
+
+               try {
+                       ServiceContext selfService = 
+                               ServiceContext.forPeer(new Peer(new MLPPeer("acumosb", "gateway.acumosb.org", "https://gateway.acumosb.org:9084", false, false, "admin@acumosab.org", "AC", "PS"), Role.SELF));
+
+                       List<MLPSolution> solutions = catalog.getSolutions(Collections.EMPTY_MAP, selfService);
+                       assertTrue("Unexpected solutions count: " + solutions.size(), solutions.size() == 5);
+                       
+                       Solution solution = catalog.getSolution("10101010-1010-1010-1010-101010101010", selfService);
+                       assertTrue("Unexpected solution info", solution.getName().equals("test"));
+
+                       List<? extends MLPSolutionRevision> revisions = solution.getRevisions();
+                       if (revisions != null && !revisions.isEmpty()) {
+                               assertTrue("Unexpected revisions count: " + revisions.size(), revisions.size() == 1);
+                               for (MLPSolutionRevision revision: revisions) {
+                                       assertTrue("Unexpected revision info", revision.getDescription().startsWith("test"));
+                                       List<MLPArtifact> artifacts = catalog.getSolutionRevisionArtifacts(solution.getSolutionId(), revision.getRevisionId());
+                                       assertTrue("Unexpected artifacts count: " + artifacts.size(), artifacts.size() == 1);
+                                               //catalog.getSolutionRevisionArtifact();
+                               }
+                       }
+
+                       //no such entry
+                       Solution nsSolution = catalog.getSolution("f0f0f0f0-f0f0-f0f0-f0f0-f0f0f0f0f0f0", selfService);
+                       assertTrue("Unexpected no such solution outcome", nsSolution == null);
+
+                       List<MLPSolutionRevision> nsRevisions = catalog.getSolutionRevisions("f0f0f0f0-f0f0-f0f0-f0f0-f0f0f0f0f0f0", selfService);
+                       assertTrue("Unexpected no such solution (revisions) outcome", nsRevisions == null);
+
+                       List<MLPArtifact> nsArtifacts = catalog.getSolutionRevisionArtifacts("f0f0f0f0-f0f0-f0f0-f0f0-f0f0f0f0f0f0", "f0f0f0f0-f0f0-f0f0-f0f0-f0f0f0f0f0f0", selfService);
+                       assertTrue("Unexpected no such solution (revision artifacts) outcome", nsArtifacts == null);
+
+                       //other errors
+                       try {
+                               catalog.getSolution("f1f1f1f1-f1f1-f1f1-f1f1-f1f1f1f1f1f1", selfService);
+                               assertTrue("Expected service exception, got none", 1 == 0);
+                       }
+                       catch (ServiceException sx) {
+                       }
+               
+                       try {
+                               catalog.getSolutionRevisions("f1f1f1f1-f1f1-f1f1-f1f1-f1f1f1f1f1f1", selfService);
+                               assertTrue("Expected service exception, got none", 1 == 0);
+                       }
+                       catch (ServiceException sx) {
+                       }
+
+                       try {
+                               catalog.getSolutionRevisionArtifacts("f1f1f1f1-f1f1-f1f1-f1f1-f1f1f1f1f1f1", "f1f1f1f1-f1f1-f1f1-f1f1-f1f1f1f1f1f1", selfService);
+                               assertTrue("Expected service exception, got none", 1 == 0);
+                       }
+                       catch (ServiceException sx) {
+                       }
+               }
+               catch (Exception x) {
+                       assertTrue("Unexpected catalog test outcome: " + x, 1 == 0);
+               }
+       }
+
+}
index 0113992..38eceac 100644 (file)
@@ -85,7 +85,8 @@ import org.acumos.cds.domain.MLPArtifact;
                                                                        "federation.ssl.key-password = acumosa",
                                                                        "federation.ssl.trust-store=classpath:acumosTrustStore.jks",
                                                                        "federation.ssl.trust-store-password=acumos",
-                                                                       "federation.ssl.client-auth=need"
+                                                                       "federation.ssl.client-auth=need",
+                                                                       "federation.registration.enabled=true"
                                                                })
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class ControllerTest {
@@ -194,6 +195,72 @@ public class ControllerTest {
                assertTrue(response.getStatusCodeValue() == 200);
                assertTrue(response.getBody().getContent().size() == 1); //no errors
        }
+       
+       @Test
+       public void testRegister() {
+
+    ((HttpComponentsClientHttpRequestFactory)
+                       this.restTemplate.getRestTemplate().getRequestFactory())
+                               .setHttpClient(prepareHttpClient("acumosc"));
+
+               ResponseEntity<JsonResponse<MLPPeer>> response =
+                       this.restTemplate.exchange("/peer/register", HttpMethod.POST, prepareRequest(), new ParameterizedTypeReference<JsonResponse<MLPPeer>>() {} );
+               if (response != null)   {
+                       log.info(EELFLoggerDelegate.debugLogger, "testRegister: {}", response.getBody());
+                       log.info(EELFLoggerDelegate.debugLogger, "testRegister: {}", response);
+               }
+       
+               assertTrue(response != null);
+               assertTrue("Expected 202 status code, got " + response.getStatusCodeValue(), response.getStatusCodeValue() == 202);
+
+               //an attempt to re-register should trigger an error
+               response =
+                       this.restTemplate.exchange("/peer/register", HttpMethod.POST, prepareRequest(), new ParameterizedTypeReference<JsonResponse<MLPPeer>>() {} );
+               if (response != null)   {
+                       log.info(EELFLoggerDelegate.debugLogger, "test(re)Register: {}", response.getBody());
+                       log.info(EELFLoggerDelegate.debugLogger, "test(re)Register: {}", response);
+               }
+       
+               assertTrue(response != null);
+               assertTrue("Expected 400 status code, got " + response.getStatusCodeValue(), response.getStatusCodeValue() == 400);
+       }
+
+       @Test
+       public void testUnregister() {
+
+    ((HttpComponentsClientHttpRequestFactory)
+                       this.restTemplate.getRestTemplate().getRequestFactory())
+                               .setHttpClient(prepareHttpClient("acumosb"));
+
+               ResponseEntity<JsonResponse<MLPPeer>> response =
+                       this.restTemplate.exchange("/peer/unregister", HttpMethod.POST, prepareRequest(), new ParameterizedTypeReference<JsonResponse<MLPPeer>>() {} );
+               if (response != null)   {
+                       log.info(EELFLoggerDelegate.debugLogger, "testUnregister: {}", response.getBody());
+                       log.info(EELFLoggerDelegate.debugLogger, "testUnregister: {}", response);
+               }
+       
+               assertTrue(response != null);
+               assertTrue("Expected 202 status code, got " + response.getStatusCodeValue(), response.getStatusCodeValue() == 202);
+       }
+
+       @Test
+       public void testUnregisterNonExistent() {
+
+    ((HttpComponentsClientHttpRequestFactory)
+                       this.restTemplate.getRestTemplate().getRequestFactory())
+                               .setHttpClient(prepareHttpClient("acumosc"));
+
+               ResponseEntity<JsonResponse<MLPPeer>> response =
+                       this.restTemplate.exchange("/peer/unregister", HttpMethod.POST, prepareRequest(), new ParameterizedTypeReference<JsonResponse<MLPPeer>>() {} );
+               if (response != null)   {
+                       log.info(EELFLoggerDelegate.debugLogger, "testUnregisterNonExistent: {}", response.getBody());
+                       log.info(EELFLoggerDelegate.debugLogger, "testUnregisterNonExistent: {}", response);
+               }
+       
+               assertTrue(response != null);
+               assertTrue("Expected 400 status code, got " + response.getStatusCodeValue(), response.getStatusCodeValue() == 400);
+       }
+
 
        @Test
        public void testPeersForbidden() {
@@ -232,10 +299,14 @@ public class ControllerTest {
        }
 
        private HttpClient prepareHttpClient() {
+               return prepareHttpClient("acumosb");
+       }
+
+       private HttpClient prepareHttpClient(String theIdentity) {
                return new InterfaceConfigurationBuilder()
                                                                .withSSL(new SSLBuilder()
-                                                                                                                       .withKeyStore("classpath:/acumosb.pkcs12")
-                                                                                                                       .withKeyStorePassword("acumosb")
+                                                                                                                       .withKeyStore("classpath:/" + theIdentity + ".pkcs12")
+                                                                                                                       .withKeyStorePassword(theIdentity)
                                                                                                                        //.withKeyPassword("acumosb")
                                                                                                                        .withTrustStore("classpath:/acumosTrustStore.jks")
                                                                                                                        .withTrustStoreType("JKS")
diff --git a/gateway/src/test/java/org/acumos/federation/gateway/test/MockResponse.java b/gateway/src/test/java/org/acumos/federation/gateway/test/MockResponse.java
new file mode 100644 (file)
index 0000000..0ae8ce4
--- /dev/null
@@ -0,0 +1,53 @@
+/*-
+ * ===============LICENSE_START=======================================================
+ * Acumos
+ * ===================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * ===================================================================================
+ * This Acumos software file is distributed by AT&T and Tech Mahindra
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ===============LICENSE_END=========================================================
+ */
+package org.acumos.federation.gateway.test;
+
+
+
+public class MockResponse {
+
+       private String  resourceName;
+       private int                     responseCode;
+       private String  responseMsg;
+
+       /** */
+       public MockResponse(int theCode, String theMessage, String theResourceName) {
+               this.responseCode = theCode;
+               this.responseMsg = theMessage;
+               this.resourceName = theResourceName;
+       }
+
+       public String getResourceName() {
+               return this.resourceName;
+       }
+
+       public int getResponseCode() {
+               return this.responseCode;
+       }
+
+       public String getResponseMsg() {
+               return this.responseMsg;
+       }
+
+       public static MockResponse success(String theResource) {
+               return new MockResponse(200, "Success", theResource);
+       }       
+}
+
diff --git a/gateway/src/test/java/org/acumos/federation/gateway/test/PeerServiceTest.java b/gateway/src/test/java/org/acumos/federation/gateway/test/PeerServiceTest.java
new file mode 100644 (file)
index 0000000..a0c53f1
--- /dev/null
@@ -0,0 +1,191 @@
+/*-
+ * ===============LICENSE_START=======================================================
+ * Acumos
+ * ===================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * ===================================================================================
+ * This Acumos software file is distributed by AT&T and Tech Mahindra
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ===============LICENSE_END=========================================================
+ */
+package org.acumos.federation.gateway.test;
+
+import java.io.InputStream;
+import java.io.Closeable;
+import java.io.IOException;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Collections;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.CountDownLatch;
+
+import org.junit.Before;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.assertj.core.api.Assertions.assertThat; 
+
+import org.junit.FixMethodOrder;
+import org.junit.runners.MethodSorters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.EventListener;
+import org.springframework.context.annotation.Bean;
+
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.core.io.ClassPathResource;
+
+import static org.mockito.Mockito.*;
+import static org.mockito.Matchers.*;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+import org.mockito.invocation.InvocationOnMock;
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.client.methods.CloseableHttpResponse; 
+import org.apache.http.message.BasicStatusLine;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+
+/* this is not good for unit testing .. */
+import org.acumos.federation.gateway.common.JsonResponse;
+import org.acumos.federation.gateway.event.PeerSubscriptionEvent;
+import org.acumos.federation.gateway.common.Clients;
+import org.acumos.federation.gateway.config.LocalInterfaceConfiguration;
+import org.acumos.federation.gateway.cds.PeerStatus;
+import org.acumos.federation.gateway.security.Peer;
+import org.acumos.federation.gateway.security.Role;
+import org.acumos.federation.gateway.service.PeerService;
+import org.acumos.federation.gateway.service.ServiceContext;
+import org.acumos.federation.gateway.service.ServiceException;
+
+import org.acumos.cds.domain.MLPPeer;
+import org.acumos.cds.domain.MLPPeerSubscription;
+
+
+/**
+ */
+
+//@RunWith(SpringJUnit4ClassRunner.class)
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = org.acumos.federation.gateway.Application.class,
+                                                               webEnvironment = WebEnvironment.RANDOM_PORT,
+                                                               properties = {
+                                                                       "federation.instance=gateway",
+                                                                       "federation.instance.name=test",
+                                                                       "federation.operator=admin",
+                                                                       "federation.ssl.key-store=classpath:acumosb.pkcs12",
+                                                                       "federation.ssl.key-store-password=acumosb",
+                                                                       "federation.ssl.key-store-type=PKCS12",
+                                                                       "federation.ssl.key-password = acumosb",
+                                                                       "federation.ssl.trust-store=classpath:acumosTrustStore.jks",
+                                                                       "federation.ssl.trust-store-password=acumos",
+                                                                       "federation.ssl.client-auth=need",
+                                                                       "local.addr=127.0.0.1",
+                                                                       "local.server.port=9011",
+                                                                       "local.ssl.key-store=classpath:acumosb.pkcs12",
+                                                                       "local.ssl.key-store-password=acumosb",
+                                                                       "local.ssl.key-store-type=PKCS12",
+                                                                       "local.ssl.key-password=acumosb",
+                                                                       "cdms.client.url=http://localhost:8000/ccds",
+                                                                       "cdms.client.username=username",
+                                                                       "cdms.client.password=password"
+                                                                       })
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class PeerServiceTest extends ServiceTest {
+
+       @Autowired
+       private PeerService peerService;
+
+       protected void initMockResponses() {
+               registerMockResponse("GET /ccds/peer/search?isSelf=true&_j=a", MockResponse.success("mockCDSPeerSearchSelfResponse.json"));
+               registerMockResponse("GET /ccds/peer?page=0&size=100", MockResponse.success("mockCDSPeerSearchAllResponse.json"));
+               //registerMockResponse("/ccds/peer", MockResponse.success("mockCDSPeerSearchAllResponse.json"));
+               registerMockResponse("GET /ccds/peer/search?subjectName=gateway.acumosa.org&_j=a", MockResponse.success("mockCDSPeerSearchResponse.json"));
+               registerMockResponse("GET /ccds/peer/search?subjectName=gateway.acumosc.org&_j=a", MockResponse.success("mockCDSSearchEmptyResponse.json"));
+               registerMockResponse("PUT /ccds/peer/a0a0a0a0-a0a0-a0a0-a0a0-a0a0a0a0a0a0", MockResponse.success("mockCDSPeerUpdateResponse.json"));
+               registerMockResponse("POST /ccds/peer", MockResponse.success("mockCDSPeerCreateResponse.json"));
+       }
+
+
+       /**
+        * The gateway behaviour is triggered by the availability of other solutions
+        * in a peer, as provided by the federation client.  
+        */
+       @Test
+       public void testPeerService() {
+
+               try {
+                       ServiceContext selfService = 
+                               ServiceContext.forPeer(new Peer(new MLPPeer("acumosb", "gateway.acumosb.org", "https://gateway.acumosb.org:9084", false, false, "admin@acumosab.org", "AC", "PS"), Role.SELF));
+
+                       List<MLPPeer> peers = peerService.getPeers(selfService);
+                       assertTrue("Unexpected all peers response", peers.size() == 2);
+
+                       List<MLPPeer> peersn = peerService.getPeerBySubjectName("gateway.acumosa.org");
+                       assertTrue("Expected one peer to be found", peersn.size() == 1);
+
+                       try {
+                               peerService.registerPeer(new MLPPeer("acumosc", "gateway.acumosc.org", "https://gateway.acumosc.org:9084", false, false, "admin@acumosc.org", "AC", "PS"));
+                       }
+                       catch(ServiceException sx) {
+                               fail("Expected peer register to succeed: " + sx + "/" + sx.getCause());
+                       }
+
+                       try {
+                               peerService.unregisterPeer(peersn.get(0));
+                       }
+                       catch(ServiceException sx) {
+                               fail("Expected peer unregister to succeed: " + sx + "/" + sx.getCause());
+                       }
+               }
+               catch(Exception sx) {
+                       fail("Unexpected peer test outcome: " + sx);
+               }
+       }
+
+}
diff --git a/gateway/src/test/java/org/acumos/federation/gateway/test/ServiceTest.java b/gateway/src/test/java/org/acumos/federation/gateway/test/ServiceTest.java
new file mode 100644 (file)
index 0000000..1c4dda1
--- /dev/null
@@ -0,0 +1,191 @@
+/*-
+ * ===============LICENSE_START=======================================================
+ * Acumos
+ * ===================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * ===================================================================================
+ * This Acumos software file is distributed by AT&T and Tech Mahindra
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ===============LICENSE_END=========================================================
+ */
+package org.acumos.federation.gateway.test;
+
+import java.io.InputStream;
+import java.io.Closeable;
+import java.io.IOException;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Collections;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.CountDownLatch;
+
+import org.junit.Before;
+import static org.junit.Assert.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat; 
+
+import org.junit.FixMethodOrder;
+import org.junit.runners.MethodSorters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.EventListener;
+import org.springframework.context.annotation.Bean;
+
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.core.io.ClassPathResource;
+
+import static org.mockito.Mockito.*;
+import static org.mockito.Matchers.*;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+import org.mockito.invocation.InvocationOnMock;
+
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.client.methods.CloseableHttpResponse; 
+import org.apache.http.message.BasicStatusLine;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+
+/* this is not good for unit testing .. */
+import org.acumos.federation.gateway.config.LocalInterfaceConfiguration;
+
+
+/**
+ * Infrastrcture code for mocking underlying REST calls, such in the case of CDS api calls.
+ */
+public abstract class ServiceTest {
+
+       //needed because service calls to other components are made over the local interface
+       @MockBean
+       private LocalInterfaceConfiguration     localConfig;
+
+       @MockBean
+       private CloseableHttpClient     localClient;
+
+       @Autowired
+       private ApplicationContext context;
+       
+       private Map<String, MockResponse> mocks = new HashMap<String, MockResponse>();
+
+       /**
+        * Derived classes should use this to register mock responses.
+        */
+       protected abstract void initMockResponses();
+
+       /**
+        * Use to register a mocked http request/response.
+        */
+       protected void registerMockResponse(String thePath, MockResponse theResponse) {
+               this.mocks.put(thePath, theResponse);
+       }
+
+
+       @Before
+       public void initTest() throws IOException {
+               MockitoAnnotations.initMocks(this);
+               initMockResponses();
+
+               when(
+                       this.localClient.execute(
+                               any(HttpUriRequest.class), any(HttpContext.class)
+                       )
+               ).thenAnswer(new Answer<HttpResponse>() {
+                               public HttpResponse answer(InvocationOnMock theInvocation) throws Throwable {
+                                       HttpUriRequest req = (HttpUriRequest)
+                                               theInvocation.getArguments()[0];
+                                       String key = req.getMethod() + " " + req.getURI().getPath() + (req.getURI().getQuery() == null ? "" : ("?" + req.getURI().getQuery()));
+
+                                       MockResponse mockResponse = mocks.get(key);
+                                       if (mockResponse == null) {
+                                               throw new IOException("Mock unhandled " + key);
+                                       }
+
+                                       BasicCloseableHttpResponse httpResponse = 
+                                               new BasicCloseableHttpResponse(
+                                                       new BasicStatusLine(
+                                                               new ProtocolVersion("HTTP",1,1), mockResponse.getResponseCode(), mockResponse.getResponseMsg()));
+
+                                       ClassPathResource resource = new ClassPathResource(mockResponse.getResourceName());
+
+                                       try {
+                                               httpResponse.setEntity(new InputStreamEntity(resource.getInputStream()));
+                                               httpResponse.addHeader("Content-Type", ContentType.APPLICATION_JSON.toString());
+                                               httpResponse.addHeader("Content-Length", String.valueOf(resource.contentLength()));
+                                       }
+                                       catch (IOException iox) {
+                                               throw new IOException("Failed to load mock resource " + resource);
+                                       }
+
+                                       return httpResponse;
+                               }
+                       });
+
+               when(
+                       this.localConfig.buildClient()
+                       )
+               .thenAnswer(new Answer<HttpClient>() {
+                               public HttpClient answer(InvocationOnMock theInvocation) {
+                                       return localClient;     
+                               }
+                       });
+       }
+
+       /**
+        * Apache's http framework expects a CloseableHttpResponse at some point and this is cheaper than mocking every time.
+        */
+       private static class BasicCloseableHttpResponse extends BasicHttpResponse implements CloseableHttpResponse {
+
+               public BasicCloseableHttpResponse(StatusLine theStatus) {
+                       super(theStatus);
+               }
+
+               @Override
+               public void close() throws IOException {
+               }
+       }
+
+}
diff --git a/gateway/src/test/resources/mockCDSDateSolutionsResponsePage0.json b/gateway/src/test/resources/mockCDSDateSolutionsResponsePage0.json
new file mode 100644 (file)
index 0000000..9936f9d
--- /dev/null
@@ -0,0 +1,1446 @@
+{
+  "content": [
+    {
+      "created": 1531381927000,
+      "modified": 1531390333000,
+      "solutionId": "02252a2f-3e21-4bdb-839a-0512a3d6bd44",
+      "name": "manipulate",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "manipulate"
+        }
+      ],
+      "webStats": {
+        "solutionId": "02252a2f-3e21-4bdb-839a-0512a3d6bd44",
+        "viewCount": 2,
+        "downloadCount": 0,
+        "lastDownload": 1531743894000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1524753544000,
+      "modified": 1529505828000,
+      "solutionId": "0632eab1-2b79-4ab9-9349-bbffdad4f5af",
+      "name": "Image_Mood",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "CL",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "1ccb5226-8b0c-4050-bb3c-64b75d17c6b1",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Test"
+        }
+      ],
+      "webStats": {
+        "solutionId": "0632eab1-2b79-4ab9-9349-bbffdad4f5af",
+        "viewCount": 13,
+        "downloadCount": 8,
+        "lastDownload": 1532704237000,
+        "ratingCount": 1,
+        "ratingAverageTenths": 30,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531385680000,
+      "modified": 1531389965000,
+      "solutionId": "086099c8-65fa-44ba-be38-c9ad36352ddf",
+      "name": "padd",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "padd"
+        }
+      ],
+      "webStats": {
+        "solutionId": "086099c8-65fa-44ba-be38-c9ad36352ddf",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531389931000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531378994000,
+      "modified": 1531381262000,
+      "solutionId": "0f963533-f8bd-4183-a30b-b2474507c297",
+      "name": "R_CMD_Model_1207",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DT",
+      "toolkitTypeCode": "RC",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tag_R_CMD"
+        }
+      ],
+      "webStats": {
+        "solutionId": "0f963533-f8bd-4183-a30b-b2474507c297",
+        "viewCount": 3,
+        "downloadCount": 2,
+        "lastDownload": 1531388484000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1529931753000,
+      "modified": 1529933535000,
+      "solutionId": "26bd497c-c833-4795-8c81-5abaaf4c9d67",
+      "name": "Sanity_250620181",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DS",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [],
+      "webStats": {
+        "solutionId": "26bd497c-c833-4795-8c81-5abaaf4c9d67",
+        "viewCount": 7,
+        "downloadCount": 0,
+        "lastDownload": 1529935650000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531382910000,
+      "modified": 1531390016000,
+      "solutionId": "26cc5a84-b888-40ff-92aa-989c4972390a",
+      "name": "subtract",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "subtract"
+        }
+      ],
+      "webStats": {
+        "solutionId": "26cc5a84-b888-40ff-92aa-989c4972390a",
+        "viewCount": 2,
+        "downloadCount": 0,
+        "lastDownload": 1531402967000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1528270856000,
+      "modified": 1528279075000,
+      "solutionId": "26cd2116-c1e1-49a7-9b2e-2200eaaf379a",
+      "name": "TestClear",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "TestClear"
+        }
+      ],
+      "webStats": {
+        "solutionId": "26cd2116-c1e1-49a7-9b2e-2200eaaf379a",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1528270991000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1529064502000,
+      "modified": 1529064807000,
+      "solutionId": "26d4ed7e-3bef-49d7-9a6a-84f131d8b3b7",
+      "name": "Sanity_15062018",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Sanity_15062018"
+        }
+      ],
+      "webStats": {
+        "solutionId": "26d4ed7e-3bef-49d7-9a6a-84f131d8b3b7",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1529064739000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531386258000,
+      "modified": 1531389628000,
+      "solutionId": "34cc31da-491a-4f52-99b8-475d82e9268a",
+      "name": "poutput",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "poutput"
+        }
+      ],
+      "webStats": {
+        "solutionId": "34cc31da-491a-4f52-99b8-475d82e9268a",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531389592000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1524561563000,
+      "modified": 1524561790000,
+      "solutionId": "38efeef1-e4f4-4298-9f68-6f0052d6ade9",
+      "name": "Predictor",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "TF",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Predictor_IST2_24042018"
+        }
+      ],
+      "webStats": {
+        "solutionId": "38efeef1-e4f4-4298-9f68-6f0052d6ade9",
+        "viewCount": 1,
+        "downloadCount": 5,
+        "lastDownload": 1524665872000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1528789299000,
+      "modified": 1528789368000,
+      "solutionId": "3ace2b0d-f1c2-439f-9e2f-f2e3e1cbc25d",
+      "name": "COMLEXSOL_120618",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "COMPEXSOL_120618"
+        }
+      ],
+      "webStats": {
+        "solutionId": "3ace2b0d-f1c2-439f-9e2f-f2e3e1cbc25d",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1528789310000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1528836682000,
+      "modified": 1528837434000,
+      "solutionId": "3f262306-41b2-4ae5-b0a9-dbd0057ed27b",
+      "name": "ATT-Image Mood Classifier",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "CL",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "1ccb5226-8b0c-4050-bb3c-64b75d17c6b1",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Sample"
+        }
+      ],
+      "webStats": {
+        "solutionId": "3f262306-41b2-4ae5-b0a9-dbd0057ed27b",
+        "viewCount": 11,
+        "downloadCount": 17,
+        "lastDownload": 1529045341000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1532590777000,
+      "modified": 1532596825000,
+      "solutionId": "3f55db61-003b-434f-bd0c-4106880d4743",
+      "name": "Image_Class_CLI_26072018",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "CL",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tag_image_classification_26718"
+        }
+      ],
+      "webStats": {
+        "solutionId": "3f55db61-003b-434f-bd0c-4106880d4743",
+        "viewCount": 8,
+        "downloadCount": 8,
+        "lastDownload": 1532608537000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531379946000,
+      "modified": 1531390554000,
+      "solutionId": "42042b12-0bbf-4dea-9b3a-77fff7aeaaaa",
+      "name": "dump_add",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "add"
+        }
+      ],
+      "webStats": {
+        "solutionId": "42042b12-0bbf-4dea-9b3a-77fff7aeaaaa",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531390529000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531380494000,
+      "modified": 1531390500000,
+      "solutionId": "43aedb81-8da0-45e7-ab72-726fa6cb2959",
+      "name": "dump_classify",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "classify"
+        }
+      ],
+      "webStats": {
+        "solutionId": "43aedb81-8da0-45e7-ab72-726fa6cb2959",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531390467000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1530609832000,
+      "modified": 1530610322000,
+      "solutionId": "472a6549-9709-4ba4-b9c3-93600d86e633",
+      "name": "complex06",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [],
+      "webStats": {
+        "solutionId": "472a6549-9709-4ba4-b9c3-93600d86e633",
+        "viewCount": 5,
+        "downloadCount": 0,
+        "lastDownload": 1530623996000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531301490000,
+      "modified": 1531302938000,
+      "solutionId": "482c985a-2e1a-48a6-8546-918e5c604ffd",
+      "name": "average",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Tag1"
+        }
+      ],
+      "webStats": {
+        "solutionId": "482c985a-2e1a-48a6-8546-918e5c604ffd",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531302870000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1529072595000,
+      "modified": 1529306949000,
+      "solutionId": "4c1ac01d-fd0e-4873-a37d-cdd14fd6539a",
+      "name": "Image_Classification_CLI_15060618",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "CL",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tag_image_classification"
+        }
+      ],
+      "webStats": {
+        "solutionId": "4c1ac01d-fd0e-4873-a37d-cdd14fd6539a",
+        "viewCount": 15,
+        "downloadCount": 8,
+        "lastDownload": 1531476747000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531133386000,
+      "modified": 1531142121000,
+      "solutionId": "4d021a48-34ba-44dc-b7d1-ddd2922ca14b",
+      "name": "R_Model_0907_IST2",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DS",
+      "toolkitTypeCode": "RC",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tag_R_Model"
+        }
+      ],
+      "webStats": {
+        "solutionId": "4d021a48-34ba-44dc-b7d1-ddd2922ca14b",
+        "viewCount": 10,
+        "downloadCount": 2,
+        "lastDownload": 1532341476000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1527682155000,
+      "modified": 1527682273000,
+      "solutionId": "515f1b30-44cd-4622-8d1f-2433178a659a",
+      "name": "FaceDetect",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DT",
+      "toolkitTypeCode": "TF",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "FaceDetect_1"
+        }
+      ],
+      "webStats": {
+        "solutionId": "515f1b30-44cd-4622-8d1f-2433178a659a",
+        "viewCount": 3,
+        "downloadCount": 4,
+        "lastDownload": 1532438035000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1525246543000,
+      "modified": 1525941197000,
+      "solutionId": "55a8c053-93f2-41a0-8796-51632a02e93e",
+      "name": "ZDBTEST",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "databroker"
+        }
+      ],
+      "webStats": {
+        "solutionId": "55a8c053-93f2-41a0-8796-51632a02e93e",
+        "viewCount": 20,
+        "downloadCount": 0,
+        "lastDownload": 1526452676000,
+        "ratingCount": 2,
+        "ratingAverageTenths": 35,
+        "featured": false
+      }
+    },
+    {
+      "created": 1530620122000,
+      "modified": 1530622834000,
+      "solutionId": "5d787336-1c2b-46a4-a2ae-ae549aa1da4b",
+      "name": "Web_R_Model_03072018",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DS",
+      "toolkitTypeCode": "RC",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "R_Model_onboarding"
+        }
+      ],
+      "webStats": {
+        "solutionId": "5d787336-1c2b-46a4-a2ae-ae549aa1da4b",
+        "viewCount": 5,
+        "downloadCount": 0,
+        "lastDownload": 1530623984000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1521193527000,
+      "modified": 1521200624000,
+      "solutionId": "614544b9-91cb-47fe-9a77-5162862a6e1e",
+      "name": "threatanalytics16-03",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DS",
+      "toolkitTypeCode": "H2",
+      "origin": null,
+      "ownerId": "d6843a76-e3dd-4e90-aca0-0ce0ece7cb11",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "testtagTA"
+        }
+      ],
+      "webStats": {
+        "solutionId": "614544b9-91cb-47fe-9a77-5162862a6e1e",
+        "viewCount": 7,
+        "downloadCount": 0,
+        "lastDownload": 1524748928000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1524560847000,
+      "modified": 1524560982000,
+      "solutionId": "62008a4d-f9f7-4460-a304-85bffa29c455",
+      "name": "Classifier",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "CL",
+      "toolkitTypeCode": "TF",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Classifier_IST2_24042018"
+        }
+      ],
+      "webStats": {
+        "solutionId": "62008a4d-f9f7-4460-a304-85bffa29c455",
+        "viewCount": 3,
+        "downloadCount": 4,
+        "lastDownload": 1525114130000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1525441190000,
+      "modified": 1525442211000,
+      "solutionId": "6414fade-da9b-431e-9646-2b842a335b58",
+      "name": "PublicTest",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "PublicTest"
+        }
+      ],
+      "webStats": {
+        "solutionId": "6414fade-da9b-431e-9646-2b842a335b58",
+        "viewCount": 9,
+        "downloadCount": 0,
+        "lastDownload": 1526047316000,
+        "ratingCount": 1,
+        "ratingAverageTenths": 50,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531386281000,
+      "modified": 1531389547000,
+      "solutionId": "649f3b2e-552d-45fe-8e21-f296963640f8",
+      "name": "predict",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [],
+      "webStats": {
+        "solutionId": "649f3b2e-552d-45fe-8e21-f296963640f8",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531389453000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1520959366000,
+      "modified": 1520959425000,
+      "solutionId": "6c863ad6-b6f3-4bbf-a79e-65a75a27db5d",
+      "name": "TestOnboardV13",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "CL",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "1cd99dd8-51be-4ee2-a005-dda9bea861ba",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Test"
+        }
+      ],
+      "webStats": {
+        "solutionId": "6c863ad6-b6f3-4bbf-a79e-65a75a27db5d",
+        "viewCount": 10,
+        "downloadCount": 11,
+        "lastDownload": 1531489990000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1532091075000,
+      "modified": 1532092039000,
+      "solutionId": "7153fcaf-3d9f-4939-9c84-dc1088f919fb",
+      "name": "PUB_07202018_WEM",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "H2",
+      "origin": null,
+      "ownerId": "54111bdc-b25d-4859-ad03-4d72ff0d7b6c",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "testPubTag"
+        }
+      ],
+      "webStats": {
+        "solutionId": "7153fcaf-3d9f-4939-9c84-dc1088f919fb",
+        "viewCount": 4,
+        "downloadCount": 0,
+        "lastDownload": 1532092626000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531228175000,
+      "modified": 1531228285000,
+      "solutionId": "770944aa-632e-45d9-9e49-08608d8e47cc",
+      "name": "Splitter",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DT",
+      "toolkitTypeCode": "SP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Splitter"
+        }
+      ],
+      "webStats": {
+        "solutionId": "770944aa-632e-45d9-9e49-08608d8e47cc",
+        "viewCount": 2,
+        "downloadCount": 0,
+        "lastDownload": 1531743934000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1525256996000,
+      "modified": 1525270495000,
+      "solutionId": "79325e62-96b2-42b4-959c-1827ed1cfe46",
+      "name": "aSentiment_Analysis_02052018",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tag_sentiment_analysis"
+        }
+      ],
+      "webStats": {
+        "solutionId": "79325e62-96b2-42b4-959c-1827ed1cfe46",
+        "viewCount": 10,
+        "downloadCount": 1,
+        "lastDownload": 1526047475000,
+        "ratingCount": 1,
+        "ratingAverageTenths": 40,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531382790000,
+      "modified": 1531390083000,
+      "solutionId": "7a0be4cb-88ca-4b37-9dda-b3dada6525cc",
+      "name": "square",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "square"
+        }
+      ],
+      "webStats": {
+        "solutionId": "7a0be4cb-88ca-4b37-9dda-b3dada6525cc",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531390055000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531380992000,
+      "modified": 1531390439000,
+      "solutionId": "7ce2eeba-c335-43fe-bb41-97829a45a46c",
+      "name": "concatenate",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "concatenate"
+        }
+      ],
+      "webStats": {
+        "solutionId": "7ce2eeba-c335-43fe-bb41-97829a45a46c",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531390381000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1522229105000,
+      "modified": 1522328104000,
+      "solutionId": "817145f9-7479-4210-a565-971904188c3b",
+      "name": "Complex",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Complexsoltest"
+        }
+      ],
+      "webStats": {
+        "solutionId": "817145f9-7479-4210-a565-971904188c3b",
+        "viewCount": 5,
+        "downloadCount": 3,
+        "lastDownload": 1522911473000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1528191651000,
+      "modified": 1528192670000,
+      "solutionId": "81e76cd9-add6-4032-9a48-bbce8cccb4f6",
+      "name": "Sanity_05062018",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Sanity_05062018"
+        }
+      ],
+      "webStats": {
+        "solutionId": "81e76cd9-add6-4032-9a48-bbce8cccb4f6",
+        "viewCount": 2,
+        "downloadCount": 0,
+        "lastDownload": 1528192262000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1528830687000,
+      "modified": 1528831262000,
+      "solutionId": "853c33e9-7a06-49ba-858f-2e220cf367c7",
+      "name": "Face_Detect",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "CL",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "1ccb5226-8b0c-4050-bb3c-64b75d17c6b1",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Sample"
+        }
+      ],
+      "webStats": {
+        "solutionId": "853c33e9-7a06-49ba-858f-2e220cf367c7",
+        "viewCount": 8,
+        "downloadCount": 9,
+        "lastDownload": 1530645835000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531226962000,
+      "modified": 1531227075000,
+      "solutionId": "8a46def9-b651-472a-a51b-76fac2e3adec",
+      "name": "Collator",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DT",
+      "toolkitTypeCode": "CO",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Collator"
+        }
+      ],
+      "webStats": {
+        "solutionId": "8a46def9-b651-472a-a51b-76fac2e3adec",
+        "viewCount": 2,
+        "downloadCount": 0,
+        "lastDownload": 1531227032000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1522247292000,
+      "modified": 1522250915000,
+      "solutionId": "8ddd620a-f06b-40fe-a8e9-3b7518603498",
+      "name": "GenericDataMapper",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DT",
+      "toolkitTypeCode": "TF",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "GenericDataMapper"
+        }
+      ],
+      "webStats": {
+        "solutionId": "8ddd620a-f06b-40fe-a8e9-3b7518603498",
+        "viewCount": 4,
+        "downloadCount": 0,
+        "lastDownload": 1532360707000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1525442385000,
+      "modified": 1525442488000,
+      "solutionId": "91dbc61f-ec4d-465b-b8d3-379db6e08865",
+      "name": "ASHTEST",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "ASHTEST"
+        }
+      ],
+      "webStats": {
+        "solutionId": "91dbc61f-ec4d-465b-b8d3-379db6e08865",
+        "viewCount": 3,
+        "downloadCount": 0,
+        "lastDownload": 1525442520000,
+        "ratingCount": 1,
+        "ratingAverageTenths": 40,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531213682000,
+      "modified": 1531214954000,
+      "solutionId": "98def80a-79a5-416c-b3f2-7c60298e8e46",
+      "name": "PUB_07102018_H2O",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "H2",
+      "origin": null,
+      "ownerId": "54111bdc-b25d-4859-ad03-4d72ff0d7b6c",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "testPubTag"
+        }
+      ],
+      "webStats": {
+        "solutionId": "98def80a-79a5-416c-b3f2-7c60298e8e46",
+        "viewCount": 6,
+        "downloadCount": 0,
+        "lastDownload": 1531302891000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1530263259000,
+      "modified": 1530263519000,
+      "solutionId": "a2b6395f-e79f-4661-bd3d-ba5200fd8654",
+      "name": "VM_Predictor_29062018",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tag_VM_Predictor_29062018"
+        }
+      ],
+      "webStats": {
+        "solutionId": "a2b6395f-e79f-4661-bd3d-ba5200fd8654",
+        "viewCount": 22,
+        "downloadCount": 1,
+        "lastDownload": 1531476667000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1532336386000,
+      "modified": 1532336386000,
+      "solutionId": "a589e693-5822-419d-bb4e-26e617b42e80",
+      "name": "Sanity_23072018",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "TestModel"
+        }
+      ],
+      "webStats": {
+        "solutionId": "a589e693-5822-419d-bb4e-26e617b42e80",
+        "viewCount": 3,
+        "downloadCount": 0,
+        "lastDownload": 1532337951000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1529931789000,
+      "modified": 1529934790000,
+      "solutionId": "a8c427a5-3443-4ec5-a722-beab54746940",
+      "name": "H2O_Web_25062018",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DT",
+      "toolkitTypeCode": "H2",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tag_web_h20_model"
+        }
+      ],
+      "webStats": {
+        "solutionId": "a8c427a5-3443-4ec5-a722-beab54746940",
+        "viewCount": 7,
+        "downloadCount": 8,
+        "lastDownload": 1529997027000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1525245763000,
+      "modified": 1532360580000,
+      "solutionId": "a923bc07-e6ef-4fa8-9496-3c6f49b97e40",
+      "name": "GenZipDataBroker",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DS",
+      "toolkitTypeCode": "BR",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [],
+      "webStats": {
+        "solutionId": "a923bc07-e6ef-4fa8-9496-3c6f49b97e40",
+        "viewCount": 5,
+        "downloadCount": 4,
+        "lastDownload": 1532375059000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1526283228000,
+      "modified": 1526290391000,
+      "solutionId": "aef71d34-d222-4901-ba94-6e71e24a396e",
+      "name": "Word_Embeddings_14052018",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tag_Word_Embeddings"
+        }
+      ],
+      "webStats": {
+        "solutionId": "aef71d34-d222-4901-ba94-6e71e24a396e",
+        "viewCount": 5,
+        "downloadCount": 0,
+        "lastDownload": 1526389369000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1528798589000,
+      "modified": 1528798939000,
+      "solutionId": "afaaed91-7434-4f9c-bf50-e22fbbe99be6",
+      "name": "CrossSell_Analytics",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tag_crosssell_analytics"
+        }
+      ],
+      "webStats": {
+        "solutionId": "afaaed91-7434-4f9c-bf50-e22fbbe99be6",
+        "viewCount": 10,
+        "downloadCount": 0,
+        "lastDownload": 1528810287000,
+        "ratingCount": 1,
+        "ratingAverageTenths": 30,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531229057000,
+      "modified": 1531389815000,
+      "solutionId": "b2f33377-2999-4422-b6e6-afb3c2ac76d9",
+      "name": "ingest",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "ingest"
+        }
+      ],
+      "webStats": {
+        "solutionId": "b2f33377-2999-4422-b6e6-afb3c2ac76d9",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531389785000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1521192173000,
+      "modified": 1521200733000,
+      "solutionId": "c197773a-1377-4573-a868-1413bd85e2d4",
+      "name": "VmPredictor",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DS",
+      "toolkitTypeCode": "PB",
+      "origin": null,
+      "ownerId": "d6843a76-e3dd-4e90-aca0-0ce0ece7cb11",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tagVM"
+        }
+      ],
+      "webStats": {
+        "solutionId": "c197773a-1377-4573-a868-1413bd85e2d4",
+        "viewCount": 8,
+        "downloadCount": 4,
+        "lastDownload": 1531476778000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531474637000,
+      "modified": 1531475892000,
+      "solutionId": "c3130c2f-3fe0-4fba-bf4e-71f2b549a2c2",
+      "name": "PUB_07132018_WEM",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "H2",
+      "origin": null,
+      "ownerId": "54111bdc-b25d-4859-ad03-4d72ff0d7b6c",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "testPubTag"
+        }
+      ],
+      "webStats": {
+        "solutionId": "c3130c2f-3fe0-4fba-bf4e-71f2b549a2c2",
+        "viewCount": 31,
+        "downloadCount": 31,
+        "lastDownload": 1532531932000,
+        "ratingCount": 4,
+        "ratingAverageTenths": 33,
+        "featured": false
+      }
+    },
+    {
+      "created": 1529590844000,
+      "modified": 1529590930000,
+      "solutionId": "c6272bad-c0f2-4e21-85e7-8ceee5576854",
+      "name": "DesignStudioDemo",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "DesignStudioDemo"
+        }
+      ],
+      "webStats": {
+        "solutionId": "c6272bad-c0f2-4e21-85e7-8ceee5576854",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1529590871000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531385703000,
+      "modified": 1531389898000,
+      "solutionId": "c7ebf361-4096-496a-8ed7-082d40e908da",
+      "name": "paverage",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "paverage"
+        }
+      ],
+      "webStats": {
+        "solutionId": "c7ebf361-4096-496a-8ed7-082d40e908da",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531389859000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    }
+  ],
+  "totalElements": 71,
+  "last": false,
+  "totalPages": 2,
+  "sort": null,
+  "size": 50,
+  "number": 0,
+  "first": true,
+  "numberOfElements": 50
+}
diff --git a/gateway/src/test/resources/mockCDSDateSolutionsResponsePage1.json b/gateway/src/test/resources/mockCDSDateSolutionsResponsePage1.json
new file mode 100644 (file)
index 0000000..a2932a6
--- /dev/null
@@ -0,0 +1,615 @@
+{
+  "content": [
+    {
+      "created": 1529663792000,
+      "modified": 1529664053000,
+      "solutionId": "c8618ba1-3ae3-4d0e-a7df-bd32277e7dcb",
+      "name": "Test_934_220618",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Test_934_22062018"
+        }
+      ],
+      "webStats": {
+        "solutionId": "c8618ba1-3ae3-4d0e-a7df-bd32277e7dcb",
+        "viewCount": 2,
+        "downloadCount": 0,
+        "lastDownload": 1529913566000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1528441761000,
+      "modified": 1528441940000,
+      "solutionId": "cd253df2-2368-4ae0-b8ea-529533845435",
+      "name": "Test_934",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "TestTag"
+        }
+      ],
+      "webStats": {
+        "solutionId": "cd253df2-2368-4ae0-b8ea-529533845435",
+        "viewCount": 7,
+        "downloadCount": 0,
+        "lastDownload": 1528456195000,
+        "ratingCount": 1,
+        "ratingAverageTenths": 40,
+        "featured": false
+      }
+    },
+    {
+      "created": 1528830995000,
+      "modified": 1528833238000,
+      "solutionId": "d8e96d4e-ade7-432c-a8ed-335627364fca",
+      "name": "ATT-Face_Privacy_Filter",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "CL",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "1ccb5226-8b0c-4050-bb3c-64b75d17c6b1",
+      "sourceId": null,
+      "tags": [],
+      "webStats": {
+        "solutionId": "d8e96d4e-ade7-432c-a8ed-335627364fca",
+        "viewCount": 8,
+        "downloadCount": 17,
+        "lastDownload": 1530645811000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1520850318000,
+      "modified": 1520850571000,
+      "solutionId": "d9f4470f-d663-4888-93ce-40585977b225",
+      "name": "Web_VM_Predictor_12032018",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "d6843a76-e3dd-4e90-aca0-0ce0ece7cb11",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "VM_Predictor"
+        }
+      ],
+      "webStats": {
+        "solutionId": "d9f4470f-d663-4888-93ce-40585977b225",
+        "viewCount": 10,
+        "downloadCount": 11,
+        "lastDownload": 1531476705000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531127757000,
+      "modified": 1531129406000,
+      "solutionId": "dc5bd274-73e0-4d22-a50d-c9b846d43758",
+      "name": "Web_2Crosssell_Analytics_09072018",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tag_cs"
+        }
+      ],
+      "webStats": {
+        "solutionId": "dc5bd274-73e0-4d22-a50d-c9b846d43758",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531129377000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1526460981000,
+      "modified": 1526461307000,
+      "solutionId": "dc9238ed-6894-4883-9f38-9df45829c97a",
+      "name": "SolDeployToAzure",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "SolDeployToAzure"
+        }
+      ],
+      "webStats": {
+        "solutionId": "dc9238ed-6894-4883-9f38-9df45829c97a",
+        "viewCount": 3,
+        "downloadCount": 0,
+        "lastDownload": 1526648038000,
+        "ratingCount": 1,
+        "ratingAverageTenths": 30,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531382611000,
+      "modified": 1531390148000,
+      "solutionId": "e1d28090-08cb-423c-872b-61ea9eb9c378",
+      "name": "output",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [],
+      "webStats": {
+        "solutionId": "e1d28090-08cb-423c-872b-61ea9eb9c378",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531390116000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1524562221000,
+      "modified": 1524562369000,
+      "solutionId": "e5594e6a-153a-4d5d-924c-ea6cc353c001",
+      "name": "Aggregator",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DT",
+      "toolkitTypeCode": "TF",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Aggregator_IST2_24042018"
+        }
+      ],
+      "webStats": {
+        "solutionId": "e5594e6a-153a-4d5d-924c-ea6cc353c001",
+        "viewCount": 2,
+        "downloadCount": 0,
+        "lastDownload": 1525276675000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531382291000,
+      "modified": 1531390216000,
+      "solutionId": "e65a9ec8-2da4-418a-a4f5-d26343789ac0",
+      "name": "multiply",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "multiply"
+        }
+      ],
+      "webStats": {
+        "solutionId": "e65a9ec8-2da4-418a-a4f5-d26343789ac0",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531390184000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1521233744000,
+      "modified": 1521238208000,
+      "solutionId": "eb86a965-5163-4abf-ab14-59c3b12cf805",
+      "name": "face_privacy_filter_detect",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DT",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "9e2def76-ab29-4125-a65c-62ff14cfc6e9",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "image"
+        },
+        {
+          "tag": "face"
+        },
+        {
+          "tag": "opencv"
+        }
+      ],
+      "webStats": {
+        "solutionId": "eb86a965-5163-4abf-ab14-59c3b12cf805",
+        "viewCount": 5,
+        "downloadCount": 2,
+        "lastDownload": 1522244471000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1530611519000,
+      "modified": 1530696648000,
+      "solutionId": "ed83b658-68b9-4297-aab2-c3bada538fb0",
+      "name": "ONAP_vm_predictor_03072018",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DT",
+      "toolkitTypeCode": "ON",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "ONAP_model_publish_04072018"
+        }
+      ],
+      "webStats": {
+        "solutionId": "ed83b658-68b9-4297-aab2-c3bada538fb0",
+        "viewCount": 10,
+        "downloadCount": 11,
+        "lastDownload": 1530787710000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531129611000,
+      "modified": 1531130043000,
+      "solutionId": "ed9079a3-543e-47e8-a9a1-2cb7b34f4c77",
+      "name": "ONAP_Image_Classification_CLI_09072018",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "CL",
+      "toolkitTypeCode": "ON",
+      "origin": null,
+      "ownerId": "c8ae58d8-d5a5-4faf-9b55-f9f46117cdb5",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tag_09072018"
+        }
+      ],
+      "webStats": {
+        "solutionId": "ed9079a3-543e-47e8-a9a1-2cb7b34f4c77",
+        "viewCount": 5,
+        "downloadCount": 11,
+        "lastDownload": 1531215432000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1528317616000,
+      "modified": 1529073941000,
+      "solutionId": "ed9df521-0010-4ab0-8019-795b999469a2",
+      "name": "iB9",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "CL",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "a1d9b9b4-657d-429a-b973-29d344ef3773",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "tag"
+        }
+      ],
+      "webStats": {
+        "solutionId": "ed9df521-0010-4ab0-8019-795b999469a2",
+        "viewCount": 2,
+        "downloadCount": 0,
+        "lastDownload": 1529317706000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1524660769000,
+      "modified": 1524734111000,
+      "solutionId": "f0424d94-1bc1-47f9-86b2-13d41e0f3374",
+      "name": "akash",
+      "description": "test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "d1e838c8-d8be-4ca4-968c-2ba5c6c04e55",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "testPubTag"
+        }
+      ],
+      "webStats": {
+        "solutionId": "f0424d94-1bc1-47f9-86b2-13d41e0f3374",
+        "viewCount": 4,
+        "downloadCount": 0,
+        "lastDownload": 1525254094000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1527154384000,
+      "modified": 1527154610000,
+      "solutionId": "f14db7a1-b4cd-4404-b636-82c5392a3e0e",
+      "name": "AzureTest_240520",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "TestTag"
+        }
+      ],
+      "webStats": {
+        "solutionId": "f14db7a1-b4cd-4404-b636-82c5392a3e0e",
+        "viewCount": 10,
+        "downloadCount": 14,
+        "lastDownload": 1527161658000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1520958197000,
+      "modified": 1520958927000,
+      "solutionId": "f23973e3-f22d-4f92-a3d1-3c1ceaf9b62a",
+      "name": "ist-to-dev-federation-test",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "CL",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "4265e711-ea85-4131-bcfa-d7817c4472e5",
+      "sourceId": null,
+      "tags": [],
+      "webStats": {
+        "solutionId": "f23973e3-f22d-4f92-a3d1-3c1ceaf9b62a",
+        "viewCount": 10,
+        "downloadCount": 1,
+        "lastDownload": 1525961070000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1524581009000,
+      "modified": 1524641638000,
+      "solutionId": "f509964b-9b11-4ab8-82ca-3a2bf009027c",
+      "name": "composite Sol",
+      "description": "Test Solution",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Test_Sol"
+        }
+      ],
+      "webStats": {
+        "solutionId": "f509964b-9b11-4ab8-82ca-3a2bf009027c",
+        "viewCount": 2,
+        "downloadCount": 0,
+        "lastDownload": 1525213181000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531386310000,
+      "modified": 1531389410000,
+      "solutionId": "f8253166-c440-4d17-abb9-0ae5c17e33d7",
+      "name": "psubtract",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "psubtract"
+        }
+      ],
+      "webStats": {
+        "solutionId": "f8253166-c440-4d17-abb9-0ae5c17e33d7",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531389370000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1528835556000,
+      "modified": 1528836476000,
+      "solutionId": "fb6d09b5-b9f6-48e4-b5a3-19c365410c64",
+      "name": "ATT-Image Classification",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "CL",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "1ccb5226-8b0c-4050-bb3c-64b75d17c6b1",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Sample"
+        }
+      ],
+      "webStats": {
+        "solutionId": "fb6d09b5-b9f6-48e4-b5a3-19c365410c64",
+        "viewCount": 5,
+        "downloadCount": 10,
+        "lastDownload": 1529046133000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531385731000,
+      "modified": 1531389702000,
+      "solutionId": "fb72cb59-3c11-4e01-b5ce-1eb1dcc19ec8",
+      "name": "pmultiply",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "pmultiply"
+        }
+      ],
+      "webStats": {
+        "solutionId": "fb72cb59-3c11-4e01-b5ce-1eb1dcc19ec8",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531389665000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1522246409000,
+      "modified": 1522246581000,
+      "solutionId": "fb7a2559-5037-4ec4-862d-28b11b333dd6",
+      "name": "AlarmGenerator",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "DT",
+      "toolkitTypeCode": "TF",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "AlarmGenerator"
+        }
+      ],
+      "webStats": {
+        "solutionId": "fb7a2559-5037-4ec4-862d-82b11b333dd6",
+        "viewCount": 3,
+        "downloadCount": 4,
+        "lastDownload": 1522663515000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    }
+  ],
+  "totalElements": 71,
+  "last": true,
+  "totalPages": 2,
+  "sort": null,
+  "size": 50,
+  "number": 1,
+  "first": false,
+  "numberOfElements": 21
+}
diff --git a/gateway/src/test/resources/mockCDSErrorResponse.json b/gateway/src/test/resources/mockCDSErrorResponse.json
new file mode 100644 (file)
index 0000000..712d9d6
--- /dev/null
@@ -0,0 +1 @@
+{"status":412,"error":"Mocked CDS server error","exception":null}
diff --git a/gateway/src/test/resources/mockCDSNoEntryWithIDResponse.json b/gateway/src/test/resources/mockCDSNoEntryWithIDResponse.json
new file mode 100644 (file)
index 0000000..6ec2b30
--- /dev/null
@@ -0,0 +1 @@
+{"status":400,"error":"No entry with ID f0f0f0f0-f0f0-f0f0-f0f0-f0f0f0f0f0f0","exception":null}
diff --git a/gateway/src/test/resources/mockCDSPeerCreateResponse.json b/gateway/src/test/resources/mockCDSPeerCreateResponse.json
new file mode 100644 (file)
index 0000000..68357a5
--- /dev/null
@@ -0,0 +1,15 @@
+{
+  "created": 1532025563000,
+  "modified": 1532636037000,
+  "peerId": "c0c0c0c0-c0c0-c0c0-c0c0-c0c0c0c0c0c0",
+  "name": "acumosc",
+  "subjectName": "gateway.acumosc.org",
+  "description": "acumosc",
+  "apiUrl": "https://gateway.acumosc.org:9084",
+  "webUrl": "https://gateway.acumosc.org:9084",
+  "contact1": "admin@acumosc.org",
+  "statusCode": "RQ",
+  "validationStatusCode": "PS",
+  "local": false,
+  "self": false
+}
diff --git a/gateway/src/test/resources/mockCDSPeerSearchAllResponse.json b/gateway/src/test/resources/mockCDSPeerSearchAllResponse.json
new file mode 100644 (file)
index 0000000..57e8501
--- /dev/null
@@ -0,0 +1,42 @@
+{
+  "content": [
+     {
+      "created": 1532025563000,
+      "modified": 1532025563000,
+      "peerId": "b0b0b0b0-b0b0-b0b0-b0b0-b0b0b0b0b0b0",
+      "name": "acumosb",
+      "subjectName": "gateway.acumosb.org",
+      "description": "test",
+      "apiUrl": "https://gateway.acumosb.org:9084",
+      "webUrl": "https://gateway.acumosb.org:9084",
+      "contact1": "admin@acumosb.org",
+      "statusCode": "AC",
+      "validationStatusCode": "PS",
+      "local": false,
+      "self": true
+    },
+    {
+      "created": 1532025563000,
+      "modified": 1532025563000,
+      "peerId": "a0a0a0a0-a0a0-a0a0-a0a0-a0a0a0a0a0a0",
+      "name": "acumosa",
+      "subjectName": "gateway.acumosa.org",
+      "description": "test",
+      "apiUrl": "https://gateway.acumosa.org:9084",
+      "webUrl": "https://gateway.acumosa.org:9084",
+      "contact1": "admin@acumosa.org",
+      "statusCode": "AC",
+      "validationStatusCode": "PS",
+      "local": false,
+      "self": false
+    }
+  ],
+  "last": true,
+  "totalPages": 1,
+  "totalElements": 2,
+  "sort": null,
+  "first": true,
+  "numberOfElements": 2,
+  "size": 100,
+  "number": 0
+}
diff --git a/gateway/src/test/resources/mockCDSPeerSearchResponse.json b/gateway/src/test/resources/mockCDSPeerSearchResponse.json
new file mode 100644 (file)
index 0000000..a432592
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "content": [
+    {
+      "created": 1532025563000,
+      "modified": 1532025563000,
+      "peerId": "a0a0a0a0-a0a0-a0a0-a0a0-a0a0a0a0a0a0",
+      "name": "acumosa",
+      "subjectName": "gateway.acumosa.org",
+      "description": "test",
+      "apiUrl": "https://gateway.acumosa.org:9084",
+      "webUrl": "https://gateway.acumosa.org:9084",
+      "contact1": "admin@acumosa.org",
+      "statusCode": "AC",
+      "validationStatusCode": "PS",
+      "local": false,
+      "self": false
+    }
+  ],
+  "last": true,
+  "totalPages": 1,
+  "totalElements": 1,
+  "sort": null,
+  "first": true,
+  "numberOfElements": 1,
+  "size": 20,
+  "number": 0
+}
diff --git a/gateway/src/test/resources/mockCDSPeerSearchSelfResponse.json b/gateway/src/test/resources/mockCDSPeerSearchSelfResponse.json
new file mode 100644 (file)
index 0000000..610e54b
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "content": [
+    {
+      "created": 1532025563000,
+      "modified": 1532025563000,
+      "peerId": "b0b0b0b0-b0b0-b0b0-b0b0-b0b0b0b0b0b0",
+      "name": "acumosb",
+      "subjectName": "gateway.acumosb.org",
+      "description": "test",
+      "apiUrl": "https://gateway.acumosb.org:9084",
+      "webUrl": "https://gateway.acumosb.org:9084",
+      "contact1": "admin@acumosb.org",
+      "statusCode": "AC",
+      "validationStatusCode": "PS",
+      "local": false,
+      "self": true
+    }
+  ],
+  "last": true,
+  "totalPages": 1,
+  "totalElements": 1,
+  "sort": null,
+  "first": true,
+  "numberOfElements": 1,
+  "size": 20,
+  "number": 0
+}
diff --git a/gateway/src/test/resources/mockCDSPeerUpdateResponse.json b/gateway/src/test/resources/mockCDSPeerUpdateResponse.json
new file mode 100644 (file)
index 0000000..4e31ffe
--- /dev/null
@@ -0,0 +1,15 @@
+{
+  "created": 1532025563000,
+  "modified": 1532636037000,
+  "peerId": "a0a0a0a0-a0a0-a0a0-a0a0-a0a0a0a0a0a0",
+  "name": "acumosa",
+  "subjectName": "gateway.acumosa.org",
+  "description": "acumosa",
+  "apiUrl": "https://gateway.acumosa.org:9084",
+  "webUrl": "https://gateway.acumosa.org:9084",
+  "contact1": "admin@acumosa.org",
+  "statusCode": "RN",
+  "validationStatusCode": "PS",
+  "local": false,
+  "self": false
+}
diff --git a/gateway/src/test/resources/mockCDSPortalSolutionsResponse.json b/gateway/src/test/resources/mockCDSPortalSolutionsResponse.json
new file mode 100644 (file)
index 0000000..441b392
--- /dev/null
@@ -0,0 +1,157 @@
+{
+  "content": [
+    {
+      "created": 1531382910000,
+      "modified": 1531390016000,
+      "solutionId": "26cc5a84-b888-40ff-92aa-989c4972390a",
+      "name": "subtract",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "subtract"
+        }
+      ],
+      "webStats": {
+        "solutionId": "26cc5a84-b888-40ff-92aa-989c4972390a",
+        "viewCount": 2,
+        "downloadCount": 0,
+        "lastDownload": 1531402967000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1528270856000,
+      "modified": 1528279075000,
+      "solutionId": "26cd2116-c1e1-49a7-9b2e-2200eaaf379a",
+      "name": "TestClear",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "TestClear"
+        }
+      ],
+      "webStats": {
+        "solutionId": "26cd2116-c1e1-49a7-9b2e-2200eaaf379a",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1528270991000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1529064502000,
+      "modified": 1529064807000,
+      "solutionId": "26d4ed7e-3bef-49d7-9a6a-84f131d8b3b7",
+      "name": "Sanity_15062018",
+      "description": "Test",
+      "provider": "Acumos",
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "CP",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Sanity_15062018"
+        }
+      ],
+      "webStats": {
+        "solutionId": "26d4ed7e-3bef-49d7-9a6a-84f131d8b3b7",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1529064739000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1531386258000,
+      "modified": 1531389628000,
+      "solutionId": "34cc31da-491a-4f52-99b8-475d82e9268a",
+      "name": "poutput",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "RG",
+      "toolkitTypeCode": "SK",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "poutput"
+        }
+      ],
+      "webStats": {
+        "solutionId": "34cc31da-491a-4f52-99b8-475d82e9268a",
+        "viewCount": 1,
+        "downloadCount": 0,
+        "lastDownload": 1531389592000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    },
+    {
+      "created": 1524561563000,
+      "modified": 1524561790000,
+      "solutionId": "38efeef1-e4f4-4298-9f68-6f0052d6ade9",
+      "name": "Predictor",
+      "description": null,
+      "provider": null,
+      "metadata": null,
+      "active": true,
+      "modelTypeCode": "PR",
+      "toolkitTypeCode": "TF",
+      "origin": null,
+      "ownerId": "fea37214-e7ef-4ec7-8c12-b10f88669a28",
+      "sourceId": null,
+      "tags": [
+        {
+          "tag": "Predictor_IST2_24042018"
+        }
+      ],
+      "webStats": {
+        "solutionId": "38efeef1-e4f4-4298-9f68-6f0052d6ade9",
+        "viewCount": 1,
+        "downloadCount": 5,
+        "lastDownload": 1524665872000,
+        "ratingCount": 0,
+        "ratingAverageTenths": 0,
+        "featured": false
+      }
+    }
+  ],
+  "last": true,
+  "totalPages": 1,
+  "totalElements": 5,
+  "sort": null,
+  "first": true,
+  "numberOfElements": 5,
+  "size": 5,
+  "number": 0
+}
diff --git a/gateway/src/test/resources/mockCDSSearchEmptyResponse.json b/gateway/src/test/resources/mockCDSSearchEmptyResponse.json
new file mode 100644 (file)
index 0000000..18a938a
--- /dev/null
@@ -0,0 +1,11 @@
+{
+  "content": [],
+  "last": true,
+  "totalPages": 1,
+  "totalElements": 0,
+  "sort": null,
+  "first": true,
+  "numberOfElements": 0,
+  "size": 20,
+  "number": 0
+}
diff --git a/gateway/src/test/resources/mockCDSSolutionResponse.json b/gateway/src/test/resources/mockCDSSolutionResponse.json
new file mode 100644 (file)
index 0000000..d7a260c
--- /dev/null
@@ -0,0 +1,29 @@
+{
+  "created": 1524660769000,
+  "modified": 1524734111000,
+  "solutionId": "10101010-1010-1010-1010-101010101010",
+  "name": "test",
+  "description": "test",
+  "provider": "Acumos",
+  "metadata": null,
+  "active": true,
+  "modelTypeCode": "PR",
+  "toolkitTypeCode": "CP",
+  "origin": null,
+  "ownerId": "d1e838c8-d8be-4ca4-968c-2ba5c6c04e55",
+  "sourceId": null,
+  "tags": [
+    {
+      "tag": "testPubTag"
+    }
+  ],
+  "webStats": {
+       "solutionId": "10101010-1010-1010-1010-101010101010",
+    "viewCount": 4,
+    "downloadCount": 0,
+    "lastDownload": 1525254094000,
+    "ratingCount": 0,
+    "ratingAverageTenths": 0,
+    "featured": false
+  }
+}
diff --git a/gateway/src/test/resources/mockCDSSolutionRevisionArtifactsResponse.json b/gateway/src/test/resources/mockCDSSolutionRevisionArtifactsResponse.json
new file mode 100644 (file)
index 0000000..bab5d33
--- /dev/null
@@ -0,0 +1,15 @@
+[
+  {
+    "created": 1524660883000,
+    "modified": 1524661041000,
+    "artifactId": "7f83159c-13b3-4ee8-ba1e-38199fca4f75",
+    "version": "1.1",
+    "artifactTypeCode": "CD",
+    "name": "acumos-cdump-f0424d94-1bc1-47f9-86b2-13d41e0f3374",
+    "description": "Cdump File for : acumos-cdump-f0424d94-1bc1-47f9-86b2-13d41e0f3374 for SolutionID : f0424d94-1bc1-47f9-86b2-13d41e0f3374 with version : 1.1",
+    "uri": "com/artifact/f0424d94-1bc1-47f9-86b2-13d41e0f3374_ACUMOS-CDUMP-F0424D94-1BC1-47F9-86B2-13D41E0F3374/1.1/f0424d94-1bc1-47f9-86b2-13d41e0f3374_ACUMOS-CDUMP-F0424D94-1BC1-47F9-86B2-13D41E0F3374-1.1.json",
+    "size": 249,
+    "metadata": null,
+    "ownerId": "d1e838c8-d8be-4ca4-968c-2ba5c6c04e55"
+  }
+]
diff --git a/gateway/src/test/resources/mockCDSSolutionRevisionsResponse.json b/gateway/src/test/resources/mockCDSSolutionRevisionsResponse.json
new file mode 100644 (file)
index 0000000..25941c4
--- /dev/null
@@ -0,0 +1,30 @@
+[
+  {
+    "created": 1524660882000,
+    "modified": 1524661041000,
+    "revisionId": "a0a0a0a0-a0a0-a0a0-a0a0-a0a0a0a0a0a0",
+    "version": "1.1",
+    "description": "test 1",
+    "metadata": null,
+    "origin": null,
+    "accessTypeCode": "PB",
+    "validationStatusCode": "PS",
+    "solutionId": "10101010-1010-1010-1010-101010101010",
+    "ownerId": "d1e838c8-d8be-4ca4-968c-2ba5c6c04e55",
+    "sourceId": null
+  },
+  {
+    "created": 1524660770000,
+    "modified": 1524660932000,
+    "revisionId": "971f15d7-6a4d-4cb7-a1eb-ce58eba03fd9",
+    "version": "1",
+    "description": "test 2",
+    "metadata": null,
+    "origin": null,
+    "accessTypeCode": "PB",
+    "validationStatusCode": "NV",
+    "solutionId": "10101010-1010-1010-1010-101010101010",
+    "ownerId": "d1e838c8-d8be-4ca4-968c-2ba5c6c04e55",
+    "sourceId": null
+  }
+]