Add list remote catalogs API 85/3985/3
authorAndrew Gauld <agauld@att.com>
Thu, 21 Mar 2019 18:28:54 +0000 (18:28 +0000)
committerAndrew Gauld <agauld@att.com>
Fri, 22 Mar 2019 16:06:37 +0000 (16:06 +0000)
New external list catalogs API: GET /catalogs
Returns list of (MLP)Catalog
In several places refactored code where copy&paste would have been needed:
Refactored *LocalServiceImpl class data loading
Refactored Peer*Controller class error handling
Refactored CatalogServiceImpl class paged data retrieval
Refactored FederationClient class peer access
Refactored Mapper class deserializer construction

Change-Id: I30013a52e3171fa31c9300fa2abee80b9a06832b
Issue-ID: ACUMOS-2575
Signed-off-by: Andrew Gauld <agauld@att.com>
27 files changed:
docs/developer-guide.rst
docs/release-notes.rst
gateway/src/main/java/org/acumos/federation/gateway/cds/Catalog.java [new file with mode: 0644]
gateway/src/main/java/org/acumos/federation/gateway/cds/Mapper.java
gateway/src/main/java/org/acumos/federation/gateway/common/API.java
gateway/src/main/java/org/acumos/federation/gateway/common/FederationClient.java
gateway/src/main/java/org/acumos/federation/gateway/controller/AbstractController.java
gateway/src/main/java/org/acumos/federation/gateway/controller/CatalogController.java
gateway/src/main/java/org/acumos/federation/gateway/controller/PeerCatalogController.java
gateway/src/main/java/org/acumos/federation/gateway/controller/PeerPeersController.java
gateway/src/main/java/org/acumos/federation/gateway/controller/PeerPingController.java
gateway/src/main/java/org/acumos/federation/gateway/controller/PeerRegistrationController.java
gateway/src/main/java/org/acumos/federation/gateway/service/CatalogService.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/AbstractServiceLocalImpl.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/CodeNamesServiceLocalImpl.java
gateway/src/main/java/org/acumos/federation/gateway/service/impl/PeerServiceLocalImpl.java
gateway/src/test/java/org/acumos/federation/gateway/test/AuthorizationTest.java
gateway/src/test/java/org/acumos/federation/gateway/test/CatalogServiceTest.java
gateway/src/test/java/org/acumos/federation/gateway/test/ContentServiceTest.java
gateway/src/test/java/org/acumos/federation/gateway/test/ControllerTest.java
gateway/src/test/java/org/acumos/federation/gateway/test/LocalControllerTest.java
gateway/src/test/java/org/acumos/federation/gateway/test/TaskTest.java
gateway/src/test/resources/mockCDSPortalCatalogsResponse.json [new file with mode: 0644]
gateway/src/test/resources/mockPeerCatalogsResponse.json [new file with mode: 0644]
gateway/src/test/resources/test-catalogs.json [new file with mode: 0644]

index 6d2100c..4c78c1b 100644 (file)
@@ -1,7 +1,7 @@
 .. ===============LICENSE_START=======================================================
 .. Acumos CC-BY-4.0
 .. ===================================================================================
-.. Copyright (C) 2017-2018 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+.. Copyright (C) 2017-2019 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
 .. ===================================================================================
 .. This Acumos documentation file is distributed by AT&T and Tech Mahindra
 .. under the Creative Commons Attribution 4.0 International License (the "License");
@@ -79,6 +79,10 @@ There is a top level envelope containing error information, and under the entry
 All identifiers are UUIDs.
 The following endpoints are defined:
 
+* /catalogs
+
+  List all visible (e.g. public) catalogs.
+
 * /solutions
 
   List all public solutions. Accepts a query parameter, 'selector', which contains a JSON object with selection criteria, base64 encoded. Acceptable selection criteria are the solution object attributes. The entries are ANDed (see :ref:`selecting`).
index 30f63b9..e463b30 100644 (file)
@@ -1,7 +1,7 @@
 .. ===============LICENSE_START=======================================================
 .. Acumos CC-BY-4.0
 .. ===================================================================================
-.. Copyright (C) 2017-2018 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+.. Copyright (C) 2017-2019 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
 .. ===================================================================================
 .. This Acumos documentation file is distributed by AT&T and Tech Mahindra
 .. under the Creative Commons Attribution 4.0 International License (the "License");
@@ -23,9 +23,12 @@ Federation Gateway Release Notes
 This server is available as a Docker image in a Docker registry at the Linux Foundation.
 The image name is "federation-gateway" and the tag is a version string as shown below. 
 
-Version 2.1.2, 2019-03-15
+Version 2.1.2, 2019-03-21
 -------------------------
 * Add JUnit test cases to reach 50% or better code coverage (`ACUMOS-2584 <https://jira.acumos.org/browse/ACUMOS-2584>`_)
+* Add API to list remote catalogs to support subscribing (`ACUMOS-2575 <https://jira.acumos.org/browse/ACUMOS-2575>`_)
+  API to list catalogs is /catalogs
+* Refactor code to avoid duplication related to implementing listing remote catalogs.
 
 Version 2.1.1, 2019-03-07
 -------------------------
diff --git a/gateway/src/main/java/org/acumos/federation/gateway/cds/Catalog.java b/gateway/src/main/java/org/acumos/federation/gateway/cds/Catalog.java
new file mode 100644 (file)
index 0000000..0f96cfa
--- /dev/null
@@ -0,0 +1,55 @@
+/*-
+ * ===============LICENSE_START=======================================================
+ * Acumos
+ * ===================================================================================
+ * Copyright (C) 2019 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.cds;
+
+import java.util.List;
+
+import org.acumos.cds.domain.MLPCatalog;
+
+/**
+ * Supplements the CDS representation of a catalog with related information: size.
+ * Allows federation to pack information passed between peers.
+ */
+public class Catalog extends MLPCatalog {
+
+       private int     size;
+
+       public Catalog() {
+       }
+
+       public Catalog(MLPCatalog theCDSCatalog) {
+               super(theCDSCatalog);
+       }
+
+       public void setSize(int theSize) {
+               size = theSize;
+       }
+
+       public int getSize() {
+               return size;
+       }
+
+       @Override
+       public String toString() {
+               return super.toString() + ", size=" + size;
+       }
+}
+
+
index 59a921c..b6b6128 100644 (file)
@@ -2,26 +2,28 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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.cds;
 
 import java.io.IOException;
 
 import org.acumos.cds.domain.MLPArtifact;
+import org.acumos.cds.domain.MLPCatalog;
 import org.acumos.cds.domain.MLPDocument;
 import org.acumos.cds.domain.MLPPeerSubscription;
 import org.acumos.cds.domain.MLPRevisionDescription;
@@ -39,112 +41,42 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
 
 
 /**
- * Provides a Jackson ObjectMapper configured with an extension module for processing
- * federation data where CDS data is declared.
+ * Provides a Jackson ObjectMapper configured with an extension module for
+ * processing federation data where CDS data is declared.
  */
+
 public class Mapper {
+       private static class SimpleModuleBuilder        {
+               private SimpleModule module = new SimpleModule("CDSModule", new Version(1, 18, 0, null));
+
+               public <B, E extends B> SimpleModuleBuilder add(Class<B> base, Class<E> enhanced) {
+                       module.addDeserializer(base, new StdDeserializer<B>(base) {
+                               @Override
+                               public B deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
+                                       return ((ObjectMapper)parser.getCodec()).readValue(parser, enhanced);
+                               }
+                       });
+                       return this;
+               }
+
+               public SimpleModule build() {
+                       return module;
+               }
+       }
 
        public static ObjectMapper build() {
+               SimpleModule fedModule = new SimpleModuleBuilder()
+                   .add(MLPArtifact.class, Artifact.class)
+                   .add(MLPCatalog.class, Catalog.class)
+                   .add(MLPDocument.class, Document.class)
+                   .add(MLPPeerSubscription.class, PeerSubscription.class)
+                   .add(MLPRevisionDescription.class, RevisionDescription.class)
+                   .add(MLPSolution.class, Solution.class)
+                   .add(MLPSolutionRevision.class, SolutionRevision.class)
+                   .build();
                ObjectMapper mapper = new ObjectMapper();
-
-               SimpleModule fedModule =
-      new SimpleModule("CDSModule",
-          new Version(1, 18, 0, null));
-    fedModule.addDeserializer(MLPSolution.class, new SolutionDeserializer());
-    fedModule.addDeserializer(MLPSolutionRevision.class, new SolutionRevisionDeserializer());
-    fedModule.addDeserializer(MLPArtifact.class, new ArtifactDeserializer());
-    fedModule.addDeserializer(MLPDocument.class, new DocumentDeserializer());
-    fedModule.addDeserializer(MLPPeerSubscription.class, new PeerSubscriptionDeserializer());
-    fedModule.addDeserializer(MLPRevisionDescription.class, new RevisionDescriptionDeserializer());
-
                mapper.registerModule(fedModule);
                mapper.registerModule(new JavaTimeModule());
-
                return mapper;
        }
-
-       private static class SolutionDeserializer extends StdDeserializer<MLPSolution> {
-
-               public SolutionDeserializer() {
-                       super(MLPSolution.class);
-               }
-
-               @Override
-       public MLPSolution deserialize(JsonParser theParser, DeserializationContext theCtx) 
-                                                                                                                                                                                               throws IOException, JsonProcessingException {
-         ObjectMapper mapper = (ObjectMapper) theParser.getCodec();
-       return mapper.readValue(theParser, Solution.class);
-       }
-       }
-
-       private static class SolutionRevisionDeserializer extends StdDeserializer<MLPSolutionRevision> {
-               public SolutionRevisionDeserializer() {
-                       super(MLPSolutionRevision.class);
-               }
-               @Override
-       public MLPSolutionRevision deserialize(JsonParser theParser, DeserializationContext theCtx) 
-                                                                                                                                                                                                       throws IOException, JsonProcessingException {
-         ObjectMapper mapper = (ObjectMapper) theParser.getCodec();
-       return mapper.readValue(theParser, SolutionRevision.class);
-       }
-       }
-
-       private static class ArtifactDeserializer extends StdDeserializer<MLPArtifact> {
-               public ArtifactDeserializer() {
-                       super(MLPArtifact.class);
-               }
-               @Override
-       public MLPArtifact deserialize(JsonParser theParser, DeserializationContext theCtx) 
-                                                                                                                                                                                               throws IOException, JsonProcessingException {
-         ObjectMapper mapper = (ObjectMapper) theParser.getCodec();
-       return mapper.readValue(theParser, Artifact.class);
-       }
-       }
-
-       private static class DocumentDeserializer extends StdDeserializer<MLPDocument> {
-               public DocumentDeserializer() {
-                       super(MLPDocument.class);
-               }
-               @Override
-       public MLPDocument deserialize(JsonParser theParser, DeserializationContext theCtx) 
-                                                                                                                                                                                               throws IOException, JsonProcessingException {
-         ObjectMapper mapper = (ObjectMapper) theParser.getCodec();
-       return mapper.readValue(theParser, Document.class);
-       }
-       }
-
-       private static class PeerSubscriptionDeserializer extends StdDeserializer<MLPPeerSubscription> {
-               public PeerSubscriptionDeserializer() {
-                       super(MLPPeerSubscription.class);
-               }
-               @Override
-       public MLPPeerSubscription deserialize(JsonParser theParser, DeserializationContext theCtx) 
-                                                                                                                                                                                               throws IOException, JsonProcessingException {
-         ObjectMapper mapper = (ObjectMapper) theParser.getCodec();
-       return mapper.readValue(theParser, PeerSubscription.class);
-       }
-       }
-
-       private static class RevisionDescriptionDeserializer extends StdDeserializer<MLPRevisionDescription> {
-               public RevisionDescriptionDeserializer() {
-                       super(MLPRevisionDescription.class);
-               }
-               @Override
-       public MLPRevisionDescription deserialize(JsonParser theParser, DeserializationContext theCtx) 
-                                                                                                                                                                                               throws IOException, JsonProcessingException {
-         ObjectMapper mapper = (ObjectMapper) theParser.getCodec();
-       return mapper.readValue(theParser, RevisionDescription.class);
-       }
-       }
 }
-
index bfc42a9..adac473 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
 package org.acumos.federation.gateway.common;
 
 import java.net.URI;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 import org.springframework.web.util.UriComponentsBuilder;
@@ -41,6 +44,7 @@ public enum API {
        SOLUTION_REVISION_DOCUMENTS(Paths.SOLUTION_REVISION_DOCUMENTS),
        DOCUMENT_DETAILS(Paths.DOCUMENT_DETAILS),
        DOCUMENT_CONTENT(Paths.DOCUMENT_CONTENT),
+       CATALOGS(Paths.CATALOGS),
        PEERS(Paths.PEERS),
        SUBSCRIPTION(Paths.SUBSCRIPTION),
        PING(Paths.PING),
@@ -53,9 +57,9 @@ public enum API {
                this.path = thePath;
        }
 
-       API(String thePath, String[] theQueryParams) {
+       API(String thePath, List<String> theQueryParams) {
                this.path = thePath;
-               this.query = theQueryParams;
+               this.query = theQueryParams.toArray(new String[theQueryParams.size()]);
        }
 
        public String path() {
@@ -169,6 +173,7 @@ public enum API {
 
                public static final String SUBSCRIPTION = "/subscription/{subscriptionId}";
 
+               public static final String CATALOGS = "/catalogs";
                public static final String PEERS = "/peers";
                public static final String PING = "/ping";
 
@@ -183,6 +188,6 @@ public enum API {
 
        public static interface Queries {
 
-               public static final String[] SOLUTIONS = { QueryParameters.SOLUTIONS_SELECTOR };
+               public static final List<String> SOLUTIONS = Collections.unmodifiableList(Arrays.asList(new String[] { QueryParameters.SOLUTIONS_SELECTOR }));
        }
 }
index 32c33d4..1c205d5 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -28,8 +28,10 @@ import java.net.URI;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 
 import org.acumos.cds.domain.MLPArtifact;
+import org.acumos.cds.domain.MLPCatalog;
 import org.acumos.cds.domain.MLPDocument;
 import org.acumos.cds.domain.MLPPeer;
 import org.acumos.cds.domain.MLPSolution;
@@ -79,61 +81,38 @@ public class FederationClient extends AbstractClient {
                super(theTarget, theClient, theMapper);
                this.client = theClient;
        }
-
        /**
         * @return Ping information from/for Remote Acumos
         * @throws FederationException
         *             Throws FederationException if remote acumos interaction has failed.
         */
-       public JsonResponse<MLPPeer> ping()
-                       throws FederationException {
-               URI uri = API.PING.buildUri(this.baseUrl);
-               log.info("Query for " + uri);
-               ResponseEntity<JsonResponse<MLPPeer>> response = null;
-               try {
-                       response = restTemplate.exchange(uri, HttpMethod.GET, null,
-                                       new ParameterizedTypeReference<JsonResponse<MLPPeer>>() {
-                                       });
-               }
-               catch (HttpStatusCodeException scx) {
-                       log.error(uri + " failed", scx);
-                       throw new PeerException(uri, scx);
-               }
-               catch (Throwable t) {
-                       log.error(uri + " unexpected failure.", t);
-                       throw new FederationException(uri, t);
-               }
-               finally {
-                       log.info(uri + " response " + response);
-               }
-               return response.getBody();
+       public JsonResponse<MLPPeer> ping() throws FederationException {
+               return handle(
+                   API.PING.buildUri(this.baseUrl),
+                   new ParameterizedTypeReference<JsonResponse<MLPPeer>>(){});
        }
 
        public JsonResponse<List<MLPPeer>> getPeers()
                        throws FederationException {
-               URI uri = API.PEERS.buildUri(this.baseUrl);
-               log.info("Query for " + uri);
-               ResponseEntity<JsonResponse<List<MLPPeer>>> response = null;
-               try {
-                       response = restTemplate.exchange(uri, HttpMethod.GET, null,
-                                       new ParameterizedTypeReference<JsonResponse<List<MLPPeer>>>() {
-                                       });
-               }
-               catch (HttpStatusCodeException scx) {
-                       log.error(uri + " failed", scx);
-                       throw new PeerException(uri, scx);
-               }
-               catch (Throwable t) {
-                       log.error(uri + " unexpected failure.", t);
-                       throw new FederationException(uri, t);
-               }
-               finally {
-                       log.info(uri + " response " + response);
-               }
-               return response.getBody();
+               return handle(
+                   API.PEERS.buildUri(this.baseUrl),
+                   new ParameterizedTypeReference<JsonResponse<List<MLPPeer>>>(){});
        }       
 
 
+       /**
+        * List catalogs in the remote Acumos.
+        *
+        * @return List of MLPCatalogs from remote Acumos.
+        *
+        * @throws FederationException
+        *             If remote acumos is not available
+        */
+       public JsonResponse<List<MLPCatalog>> getCatalogs() throws FederationException {
+               return handle(
+                   API.CATALOGS.buildUri(this.baseUrl),
+                   new ParameterizedTypeReference<JsonResponse<List<MLPCatalog>>>(){});
+       }
        /**
         * 
         * @param theSelection
@@ -150,7 +129,6 @@ public class FederationClient extends AbstractClient {
                try {
                        log.info("getSolutions selector {}", Utils.mapToJsonString(theSelection));
                        selectorParam = theSelection == null ? null
-                                       // : UriUtils.encodeQueryParam(Utils.mapToJsonString(theSelection),"UTF-8");
                                        : Base64Utils.encodeToString(Utils.mapToJsonString(theSelection).getBytes("UTF-8"));
                        log.info("getSolutions encoded selector {}", selectorParam);
                }
@@ -158,27 +136,8 @@ public class FederationClient extends AbstractClient {
                        throw new IllegalArgumentException("Cannot process the selection argument", x);
                }
 
-               URI uri = API.SOLUTIONS.buildUri(this.baseUrl, selectorParam == null ? Collections.EMPTY_MAP
-                               : Collections.singletonMap(API.QueryParameters.SOLUTIONS_SELECTOR, selectorParam));
-               log.info("Query for " + uri);
-               ResponseEntity<JsonResponse<List<MLPSolution>>> response = null;
-               try {
-                       response = restTemplate.exchange(uri, HttpMethod.GET, null,
-                                       new ParameterizedTypeReference<JsonResponse<List<MLPSolution>>>() {
-                                       });
-               }
-               catch (HttpStatusCodeException scx) {
-                       log.error(uri + " failed", scx);
-                       throw new PeerException(uri, scx);
-               }
-               catch (Throwable t) {
-                       log.error(uri + " unexpected failure.", t);
-                       throw new FederationException(uri, t);
-               }
-               finally {
-                       log.info(uri + " response " + response);
-               }
-               return response.getBody();
+               URI uri = API.SOLUTIONS.buildUri(this.baseUrl, selectorParam == null ? Collections.EMPTY_MAP : Collections.singletonMap(API.QueryParameters.SOLUTIONS_SELECTOR, selectorParam));
+               return handle(uri, new ParameterizedTypeReference<JsonResponse<List<MLPSolution>>>() {});
        }
 
        /**
@@ -190,26 +149,9 @@ public class FederationClient extends AbstractClient {
        public JsonResponse<MLPSolution> getSolution(String theSolutionId)
                        throws FederationException {
 
-               URI uri = API.SOLUTION_DETAIL.buildUri(this.baseUrl, theSolutionId);
-               log.info("Query for " + uri);
-               ResponseEntity<JsonResponse<MLPSolution>> response = null;
-               try {
-                       response = restTemplate.exchange(uri, HttpMethod.GET, null,
-                                       new ParameterizedTypeReference<JsonResponse<MLPSolution>>() {
-                                       });
-               }
-               catch (HttpStatusCodeException scx) {
-                       log.error(uri + " failed", scx);
-                       throw new PeerException(uri, scx);
-               }
-               catch (Throwable t) {
-                       log.error(uri + " unexpected failure.", t);
-                       throw new FederationException(uri, t);
-               }
-               finally {
-                       log.info(uri + " response " + response);
-               }
-               return response.getBody();
+               return handle(
+                   API.SOLUTION_DETAIL.buildUri(this.baseUrl, theSolutionId),
+                   new ParameterizedTypeReference<JsonResponse<MLPSolution>>() {});
        }
 
        /**
@@ -222,26 +164,9 @@ public class FederationClient extends AbstractClient {
        public JsonResponse<List<MLPSolutionRevision>> getSolutionRevisions(String theSolutionId)
                        throws FederationException {
 
-               URI uri = API.SOLUTION_REVISIONS.buildUri(this.baseUrl, theSolutionId);
-               log.info("Query for " + uri);
-               ResponseEntity<JsonResponse<List<MLPSolutionRevision>>> response = null;
-               try {
-                       response = restTemplate.exchange(uri, HttpMethod.GET, null,
-                                       new ParameterizedTypeReference<JsonResponse<List<MLPSolutionRevision>>>() {
-                                       });
-               }
-               catch (HttpStatusCodeException scx) {
-                       log.error(uri + " failed", scx);
-                       throw new PeerException(uri, scx);
-               }
-               catch (Throwable t) {
-                       log.info(uri + " unexpected failure.", t);
-                       throw new FederationException(uri, t);
-               }
-               finally {
-                       log.info(uri + " response " + response);
-               }
-               return response.getBody();
+               return handle(
+                   API.SOLUTION_REVISIONS.buildUri(this.baseUrl, theSolutionId),
+                   new ParameterizedTypeReference<JsonResponse<List<MLPSolutionRevision>>>() {});
        }
 
        /**
@@ -256,26 +181,9 @@ public class FederationClient extends AbstractClient {
        public JsonResponse<MLPSolutionRevision> getSolutionRevision(String theSolutionId, String theRevisionId)
                        throws FederationException {
 
-               URI uri = API.SOLUTION_REVISION_DETAILS.buildUri(this.baseUrl, theSolutionId, theRevisionId);
-               log.info("Query for " + uri);
-               ResponseEntity<JsonResponse<MLPSolutionRevision>> response = null;
-               try {
-                       response = restTemplate.exchange(uri, HttpMethod.GET, null,
-                                       new ParameterizedTypeReference<JsonResponse<MLPSolutionRevision>>() {
-                                       });
-               }
-               catch (HttpStatusCodeException scx) {
-                       log.error(uri + " failed", scx);
-                       throw new PeerException(uri, scx);
-               }
-               catch (Throwable t) {
-                       log.info(uri + " unexpected failure.", t);
-                       throw new FederationException(uri, t);
-               }
-               finally {
-                       log.info(uri + " response " + response);
-               }
-               return response.getBody();
+               return handle(
+                    API.SOLUTION_REVISION_DETAILS.buildUri(this.baseUrl, theSolutionId, theRevisionId),
+                    new ParameterizedTypeReference<JsonResponse<MLPSolutionRevision>>() {});
        }
 
        /**
@@ -288,28 +196,10 @@ public class FederationClient extends AbstractClient {
         * @throws FederationException
         *             if remote acumos is not available
         */
-       public JsonResponse<List<MLPArtifact>> getArtifacts(String theSolutionId, String theRevisionId)
-                       throws FederationException {
-               URI uri = API.SOLUTION_REVISION_ARTIFACTS.buildUri(this.baseUrl, theSolutionId, theRevisionId);
-               log.info("Query for " + uri);
-               ResponseEntity<JsonResponse<List<MLPArtifact>>> response = null;
-               try {
-                       response = restTemplate.exchange(uri, HttpMethod.GET, null,
-                                       new ParameterizedTypeReference<JsonResponse<List<MLPArtifact>>>() {
-                                       });
-               }
-               catch (HttpStatusCodeException scx) {
-                       log.error(uri + " failed", scx);
-                       throw new PeerException(uri, scx);
-               }
-               catch (Throwable t) {
-                       log.error(uri + " unexpected failure.", t);
-                       throw new FederationException(uri, t);
-               }
-               finally {
-                       log.info(uri + " response " + response);
-               }
-               return response.getBody();
+       public JsonResponse<List<MLPArtifact>> getArtifacts(String theSolutionId, String theRevisionId) throws FederationException {
+               return handle(
+                   API.SOLUTION_REVISION_ARTIFACTS.buildUri(this.baseUrl, theSolutionId, theRevisionId),
+                   new ParameterizedTypeReference<JsonResponse<List<MLPArtifact>>>() {});
        }
 
        /**
@@ -322,8 +212,7 @@ public class FederationClient extends AbstractClient {
         * @return Resource
         * @throws FederationException On failure
         */
-       public Resource getArtifactContent(String theSolutionId, String theRevisionId, String theArtifactId)
-                                                                                                                                                                                                                                                                                                                                                       throws FederationException {
+       public Resource getArtifactContent(String theSolutionId, String theRevisionId, String theArtifactId) throws FederationException {
                return download2(API.ARTIFACT_CONTENT.buildUri(this.baseUrl, theSolutionId, theRevisionId, theArtifactId));
        }
 
@@ -337,28 +226,10 @@ public class FederationClient extends AbstractClient {
         * @throws FederationException
         *             if remote acumos is not available
         */
-       public JsonResponse<List<MLPDocument>> getDocuments(String theSolutionId, String theRevisionId)
-                       throws FederationException {
-               URI uri = API.SOLUTION_REVISION_DOCUMENTS.buildUri(this.baseUrl, theSolutionId, theRevisionId);
-               log.info("Query for " + uri);
-               ResponseEntity<JsonResponse<List<MLPDocument>>> response = null;
-               try {
-                       response = restTemplate.exchange(uri, HttpMethod.GET, null,
-                                       new ParameterizedTypeReference<JsonResponse<List<MLPDocument>>>() {
-                                       });
-               }
-               catch (HttpStatusCodeException scx) {
-                       log.error(uri + " failed", scx);
-                       throw new PeerException(uri, scx);
-               }
-               catch (Throwable t) {
-                       log.error(uri + " unexpected failure.", t);
-                       throw new FederationException(uri, t);
-               }
-               finally {
-                       log.info(uri + " response " + response);
-               }
-               return response == null ? null : response.getBody();
+       public JsonResponse<List<MLPDocument>> getDocuments(String theSolutionId, String theRevisionId) throws FederationException {
+               return handle(
+                   API.SOLUTION_REVISION_DOCUMENTS.buildUri(this.baseUrl, theSolutionId, theRevisionId),
+                   new ParameterizedTypeReference<JsonResponse<List<MLPDocument>>>() {});
        }
 
        /**
@@ -372,37 +243,63 @@ public class FederationClient extends AbstractClient {
         * @throws FederationException
         *             On failure
         */
-       public Resource getDocumentContent(String theSolutionId, String theRevisionId, String theDocumentId)
-                                                                                                                                                                                                                                                                                                                                               throws FederationException {
+       public Resource getDocumentContent(String theSolutionId, String theRevisionId, String theDocumentId) throws FederationException {
                return download2(API.DOCUMENT_CONTENT.buildUri(this.baseUrl, theSolutionId, theRevisionId, theDocumentId));
        }
 
-       protected Resource download(URI theUri) throws FederationException {
-               log.info("Query for download {}", theUri);
-               ResponseEntity<Resource> response = null;
-               RequestEntity<Void> request = RequestEntity
-                                                                                                                                       .get(theUri)
-                                                                                                                                       .accept(MediaType.ALL)
-                                                                                                                                       .build();
+       /**
+        * @param theSelf self
+        * @return Register self with the peer this client points to.
+        * @throws FederationException
+        *             if remote acumos is not available
+        */
+       public JsonResponse<MLPPeer> register(MLPPeer theSelf) throws FederationException {
+               return handle(
+                   API.PEER_REGISTER.buildUri(this.baseUrl),
+                   new ParameterizedTypeReference<JsonResponse<MLPPeer>>() {});
+       }       
+
+       /**
+        * Handle errors when making requests to remote Acumos.
+        * @param uri the URI on the remote Acumos
+        * @param fcn the function to make the remote call
+        * @return the body of the response from the remote call
+        * @throws FederationException if remote acumos is not available
+        */
+       private <T> T handle(URI uri, Supplier<ResponseEntity<T>> fcn) throws FederationException {
                try {
-                       response = restTemplate.exchange(request, Resource.class);
-               }
-               catch (HttpStatusCodeException scx) {
-                       log.error(theUri + " failed", scx);
-                       throw new PeerException(theUri, scx);
-               }
-               catch (Throwable t) {
-                       log.error(theUri + " unexpected failure.", t);
-                       throw new FederationException(theUri, t);
-               }
-               finally {
-                       log.info(theUri + " response " + response);
+                       log.info("Query for {}", uri);
+                       ResponseEntity<T> response = fcn.get();
+                       log.debug("{} response {}", uri, response);
+                       return response == null? null: response.getBody();
+               } catch (HttpStatusCodeException scx) {
+                       log.error(uri + " failed", scx);
+                       throw new PeerException(uri, scx);
+               } catch (Throwable t) {
+                       log.error(uri + " unexpected failure.", t);
+                       throw new FederationException(uri, t);
                }
+       }
 
-               return response.getBody();
+       /**
+        * Handle common part of making most requests to remote Acumos.
+        * @param uri the URI on the remote Acumos
+        * @param type parameterized type reference for parsing return type
+        * @return the body of the response from the remote call
+        * @throws FederationException if remote acumos is not available
+        */
+       private <T> T handle(URI uri, ParameterizedTypeReference<T> type) throws FederationException {
+               return handle(uri, () -> restTemplate.exchange(uri, HttpMethod.GET, null, type));
+       }
+
+       protected Resource download(URI theUri) throws FederationException {
+               RequestEntity<Void> request = RequestEntity.get(theUri).accept(MediaType.ALL).build();
+               return handle(theUri, () -> restTemplate.exchange(request, Resource.class));
        }
 
        /**
+        * Download content of a URI.
+        * E.g. the body of an artifact or document.
         * Important: the Resource returned by this method MUST BE CLOSED by whoever uses it.
         * @param theUri URI
         * @return Resource
@@ -429,8 +326,7 @@ public class FederationClient extends AbstractClient {
                }
        }
 
-       public static class StreamingResource extends InputStreamResource
-                                                                                                                                                               implements Closeable {
+       public static class StreamingResource extends InputStreamResource implements Closeable {
 
                private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
                private ClientHttpResponse response;
@@ -457,35 +353,4 @@ public class FederationClient extends AbstractClient {
                        this.response.close();
                }
        }
-
-
-
-       /**
-        * @param theSelf self
-        * @return Register self with the peer this client points to.
-        * @throws HttpStatusCodeException
-        *             Throws HttpStatusCodeException if remote acumos interaction has failed.
-        */
-       public JsonResponse<MLPPeer> register(MLPPeer theSelf)
-                       throws HttpStatusCodeException {
-               URI uri = API.PEER_REGISTER.buildUri(this.baseUrl);
-               log.info("Query for " + uri);
-               ResponseEntity<JsonResponse<MLPPeer>> response = null;
-               try {
-                       response = restTemplate.exchange(uri, HttpMethod.GET, null,
-                                       new ParameterizedTypeReference<JsonResponse<MLPPeer>>() {
-                                       });
-               }
-               catch (HttpStatusCodeException x) {
-                       log.error(uri + " failed", x);
-                       throw x;
-               }
-               catch (Throwable t) {
-                       log.error(uri + " unexpected failure.", t);
-               }
-               finally {
-                       log.info(uri + " response " + response);
-               }
-               return response == null ? null : response.getBody();
-       }       
 }
index 52998b9..e0adc9e 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
 
 package org.acumos.federation.gateway.controller;
 
+import java.lang.invoke.MethodHandles;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+
+import org.acumos.cds.domain.MLPPeer;
+
+import org.acumos.federation.gateway.common.Clients;
+import org.acumos.federation.gateway.common.FederationClient;
+import org.acumos.federation.gateway.common.JsonResponse;
+import org.acumos.federation.gateway.service.PeerService;
 
 
-/**
- * 
- *
- */
 public abstract class AbstractController {
+       private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-       protected static final String APPLICATION_JSON = "application/json";
+       @FunctionalInterface
+       protected interface ThrowingFunction<T, R> {
+               R apply(T t) throws Exception;
+       }
+
+       @Autowired
+       protected Clients clients;
+       @Autowired
+       protected PeerService peerService;
+
+       protected static final String APPLICATION_JSON = MediaType.APPLICATION_JSON_UTF8_VALUE;
+       protected static final String APPLICATION_OCTET_STREAM = MediaType.APPLICATION_OCTET_STREAM_VALUE;
 
        protected final ObjectMapper mapper;
 
@@ -37,4 +59,29 @@ public abstract class AbstractController {
                mapper = new ObjectMapper();
        }
 
+       /**
+        * Handle common aspects of forwarding a local request to a peer Acumos.
+        * @param opname the name of the operation to perform
+        * @param response the HTTP response for setting error codes
+        * @param peerId the ID of the remote peer to call
+        * @param fcn the operation to invoke on the remote peer
+        */
+       protected <T> JsonResponse<T> callPeer(String opname, HttpServletResponse response, String peerId, ThrowingFunction<FederationClient, JsonResponse<T>> fcn) {
+               try {
+                       MLPPeer peer = peerService.getPeerById(peerId);
+                       if (peer == null) {
+                               response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+                               return JsonResponse.<T>buildErrorResponse()
+                                   .withMessage("No peer with id " + peerId + " found.")
+                                   .build();
+                       }
+                       JsonResponse<T> ret = fcn.apply(clients.getFederationClient(peer.getApiUrl()));
+                       response.setStatus(HttpServletResponse.SC_OK);
+                       return ret;
+               } catch (Exception x) {
+                       response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                       log.error("Exception occurred during peer " + peerId + " " + opname, x);
+                       return JsonResponse.<T>buildErrorResponse().withError(x).build();
+               }
+       }
 }
index ed6a2a2..4fba274 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -31,6 +31,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.acumos.cds.domain.MLPArtifact;
+import org.acumos.cds.domain.MLPCatalog;
 import org.acumos.cds.domain.MLPDocument;
 import org.acumos.cds.domain.MLPSolution;
 import org.acumos.cds.domain.MLPSolutionRevision;
@@ -47,7 +48,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.io.Resource;
-import org.springframework.http.MediaType;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Controller;
 import org.springframework.util.Base64Utils;
@@ -75,6 +75,40 @@ public class CatalogController extends AbstractController {
        @Autowired
        private ContentService contentService;
 
+       /**
+        * @param theHttpRequest Request
+        * @param theHttpResponse
+        *            HttpServletResponse
+        * @return List of Catalogs in JSON format.
+        */
+       @CrossOrigin
+       @PreAuthorize("isActive && hasAuthority(T(org.acumos.federation.gateway.security.Priviledge).CATALOG_ACCESS)")
+       @ApiOperation(value = "Invoked by Peer Acumos to get a list of visible Catalogs from the local Acumos Instance .", response = MLPCatalog.class, responseContainer = "List")
+       @RequestMapping(value = { API.Paths.CATALOGS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
+       @ResponseBody
+       public JsonResponse<List<MLPCatalog>> getCatalogs(HttpServletRequest theHttpRequest, HttpServletResponse theHttpResponse) {
+               JsonResponse<List<MLPCatalog>> response = null;
+               List<MLPCatalog> catalogs = null;
+               log.debug(API.Paths.CATALOGS);
+               try {
+                       log.debug("getCatalogs");
+                       catalogs = catalogService.getCatalogs(new ControllerContext());
+                       response = JsonResponse.<List<MLPCatalog>> buildResponse()
+                                                                                                               .withMessage("available catalogs")
+                                                                                                               .withContent(catalogs)
+                                                                                                               .build();
+                       theHttpResponse.setStatus(HttpServletResponse.SC_OK);
+                       log.debug("getCatalogs: provided {} catalogs", catalogs == null ? 0 : catalogs.size());
+               } catch (Throwable x) {
+                       response = JsonResponse.<List<MLPCatalog>> buildErrorResponse()
+                                                                                                                .withError(x)
+                                                                                                                .build();
+                       theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                       log.error("Exception occurred while fetching catalogs", x);
+               }
+               return response;
+       }
+
        /**
         * @param theHttpRequest Request
         * @param theHttpResponse
@@ -84,10 +118,9 @@ public class CatalogController extends AbstractController {
         * @return List of Published ML Solutions in JSON format.
         */
        @CrossOrigin
-       // @PreAuthorize("hasAuthority('PEER')"
        @PreAuthorize("isActive && hasAuthority(T(org.acumos.federation.gateway.security.Priviledge).CATALOG_ACCESS)")
        @ApiOperation(value = "Invoked by Peer Acumos to get a list of Published Solutions from the Catalog of the local Acumos Instance .", response = MLPSolution.class, responseContainer = "List")
-       @RequestMapping(value = { API.Paths.SOLUTIONS }, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+       @RequestMapping(value = { API.Paths.SOLUTIONS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
        public JsonResponse<List<MLPSolution>> getSolutions(
                        HttpServletRequest theHttpRequest,
@@ -284,8 +317,7 @@ public class CatalogController extends AbstractController {
        @CrossOrigin
        @PreAuthorize("isActive && hasAuthority('CATALOG_ACCESS')")
        @ApiOperation(value = "Invoked by Peer Acumos to get a list of solution revision artifacts from the local Acumos Instance .", response = MLPArtifact.class, responseContainer = "List")
-       @RequestMapping(value = {
-                       API.Paths.SOLUTION_REVISION_ARTIFACTS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
+       @RequestMapping(value = { API.Paths.SOLUTION_REVISION_ARTIFACTS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
        public JsonResponse<List<MLPArtifact>> getSolutionRevisionArtifacts(
                        HttpServletRequest theHttpRequest, HttpServletResponse theHttpResponse,
@@ -341,8 +373,7 @@ public class CatalogController extends AbstractController {
        @CrossOrigin
        @PreAuthorize("isActive && hasAuthority('CATALOG_ACCESS')")
        @ApiOperation(value = "Invoked by Peer Acumos to get a list of solution revision public documents from the local Acumos Instance .", response = MLPArtifact.class, responseContainer = "List")
-       @RequestMapping(value = {
-                       API.Paths.SOLUTION_REVISION_DOCUMENTS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
+       @RequestMapping(value = { API.Paths.SOLUTION_REVISION_DOCUMENTS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
        public JsonResponse<List<MLPDocument>> getSolutionRevisionDocuments(
                        HttpServletRequest theHttpRequest, HttpServletResponse theHttpResponse,
@@ -401,8 +432,7 @@ public class CatalogController extends AbstractController {
        @CrossOrigin
        @PreAuthorize("isActive && hasAuthority('CATALOG_ACCESS')")
        @ApiOperation(value = "API to download artifact content", response = Resource.class, code = 200)
-       @RequestMapping(value = {
-                       API.Paths.ARTIFACT_CONTENT }, method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
+       @RequestMapping(value = { API.Paths.ARTIFACT_CONTENT }, method = RequestMethod.GET, produces = APPLICATION_OCTET_STREAM)
        @ResponseBody
        public Callable<Resource> getArtifactContent(HttpServletRequest theHttpRequest,
                        HttpServletResponse theHttpResponse, @PathVariable("solutionId") String theSolutionId,
@@ -444,8 +474,7 @@ public class CatalogController extends AbstractController {
        @CrossOrigin
        @PreAuthorize("isActive && hasAuthority('CATALOG_ACCESS')")
        @ApiOperation(value = "API to download a documents' content", response = Resource.class, code = 200)
-       @RequestMapping(value = {
-                       API.Paths.DOCUMENT_CONTENT }, method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
+       @RequestMapping(value = { API.Paths.DOCUMENT_CONTENT }, method = RequestMethod.GET, produces = APPLICATION_OCTET_STREAM)
        @ResponseBody
        public Callable<Resource> getDocumentContent(HttpServletRequest theHttpRequest,
                        HttpServletResponse theHttpResponse, @PathVariable("solutionId") String theSolutionId,
index c150059..de3a17f 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -22,20 +22,17 @@ package org.acumos.federation.gateway.controller;
 
 import java.lang.invoke.MethodHandles;
 import java.util.List;
-import java.util.Map;
 
 import javax.servlet.http.HttpServletResponse;
 
+import org.acumos.cds.domain.MLPCatalog;
 import org.acumos.cds.domain.MLPPeer;
 import org.acumos.cds.domain.MLPSolution;
 import org.acumos.federation.gateway.common.API;
-import org.acumos.federation.gateway.common.Clients;
 import org.acumos.federation.gateway.common.JsonResponse;
-import org.acumos.federation.gateway.service.PeerService;
 import org.acumos.federation.gateway.util.Utils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Controller;
 import org.springframework.util.Base64Utils;
@@ -59,10 +56,24 @@ public class PeerCatalogController extends AbstractController {
 
        private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-       @Autowired
-       private Clients clients;
-       @Autowired
-       private PeerService peerService;
+       /**
+        * Provides access 
+        * @param theHttpResponse
+        *            HttpServletResponse
+        * @param thePeerId Peer ID
+        * @return List of Catalogs in JSON format.
+        */
+       @CrossOrigin
+       @PreAuthorize("hasAuthority(T(org.acumos.federation.gateway.security.Priviledge).PEER_ACCESS)")
+       @ApiOperation(value = "Invoked by local Acumos to get a list of catalogs available from a peer Acumos instance .", response = MLPCatalog.class, responseContainer = "List")
+       @RequestMapping(value = { API.Paths.CATALOGS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
+       @ResponseBody
+       public JsonResponse<List<MLPCatalog>> getCatalogs(
+           HttpServletResponse theHttpResponse,
+           @PathVariable("peerId") String thePeerId) {
+               log.debug(API.Roots.LOCAL + "" + API.Paths.CATALOGS);
+               return callPeer("getCatalogs", theHttpResponse, thePeerId, peer -> peer.getCatalogs());
+       }
 
        /**
         * Provides access 
@@ -79,30 +90,11 @@ public class PeerCatalogController extends AbstractController {
        @RequestMapping(value = { API.Paths.SOLUTIONS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
        public JsonResponse<List<MLPSolution>> getSolutions(
-                       /* HttpServletRequest theHttpRequest, */
-                       HttpServletResponse theHttpResponse,
-                       @PathVariable("peerId") String thePeerId,
-                       @RequestParam(value = API.QueryParameters.SOLUTIONS_SELECTOR, required = false) String theSelector) {
+           HttpServletResponse theHttpResponse,
+           @PathVariable("peerId") String thePeerId,
+           @RequestParam(value = API.QueryParameters.SOLUTIONS_SELECTOR, required = false) String theSelector) {
                log.debug(API.Roots.LOCAL + "" + API.Paths.SOLUTIONS);
-               JsonResponse<List<MLPSolution>> response = null;
-               try {
-                       MLPPeer peer = this.peerService.getPeerById(thePeerId);
-                       Map<String, Object> selector = null;
-                       if (theSelector != null)
-                               selector = Utils.jsonStringToMap(new String(Base64Utils.decodeFromString(theSelector), "UTF-8"));
-                       response = this.clients.getFederationClient(peer.getApiUrl()).getSolutions(selector);
-                       
-                       theHttpResponse.setStatus(HttpServletResponse.SC_OK);
-               }
-               //catch (ServiceException sx) {}
-               catch (Exception x) {
-                       response = JsonResponse.<List<MLPSolution>> buildErrorResponse()
-                                                                                                                .withError(x)
-                                                                                                                .build();
-                       theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-                       log.error("An error occurred while processing a local peer solutions request for peer " + thePeerId, x);
-               }
-               return response;
+               return callPeer("getSolutions", theHttpResponse, thePeerId, peer -> peer.getSolutions(theSelector == null? null: Utils.jsonStringToMap(new String(Base64Utils.decodeFromString(theSelector), "UTF-8"))));
        }
 
        @CrossOrigin
@@ -111,24 +103,10 @@ public class PeerCatalogController extends AbstractController {
        @RequestMapping(value = { API.Paths.SOLUTION_DETAILS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
        public JsonResponse<MLPSolution> getSolutionDetails(
-                       HttpServletResponse theHttpResponse,
-                       @PathVariable("peerId") String thePeerId,
-                       @PathVariable(value = "solutionId") String theSolutionId) {
+           HttpServletResponse theHttpResponse,
+           @PathVariable("peerId") String thePeerId,
+           @PathVariable(value = "solutionId") String theSolutionId) {
                log.debug(API.Roots.LOCAL + "" + API.Paths.SOLUTION_DETAILS);
-               JsonResponse<MLPSolution> response = null;
-               try {
-                       MLPPeer peer = this.peerService.getPeerById(thePeerId);
-                       response = this.clients.getFederationClient(peer.getApiUrl()).getSolution(theSolutionId);
-                       
-                       theHttpResponse.setStatus(HttpServletResponse.SC_OK);
-               }
-               catch (Exception x) {
-                       response = JsonResponse.<MLPSolution> buildErrorResponse()
-                                                                                                                .withError(x)
-                                                                                                                .build();
-                       theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-                       log.error("An error occurred while fetching solution " + theSolutionId + " from peer " + thePeerId, x);
-               }
-               return response;
+               return callPeer("getSolution", theHttpResponse, thePeerId, peer -> peer.getSolution(theSolutionId));
        }
 }
index 2f7a28f..75bd7a2 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -27,12 +27,9 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.acumos.cds.domain.MLPPeer;
 import org.acumos.federation.gateway.common.API;
-import org.acumos.federation.gateway.common.Clients;
 import org.acumos.federation.gateway.common.JsonResponse;
-import org.acumos.federation.gateway.service.PeerService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.CrossOrigin;
@@ -49,12 +46,6 @@ public class PeerPeersController extends AbstractController {
 
        private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-       @Autowired
-       private Clients clients;
-       @Autowired
-       private PeerService peerService;
-       
-
        /**
         * Allows local components to ping a peer.
         * @param theHttpResponse
@@ -68,26 +59,11 @@ public class PeerPeersController extends AbstractController {
        @RequestMapping(value = { API.Paths.PEERS }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
        public JsonResponse<List<MLPPeer>> getPeers(
-                       /* HttpServletRequest theHttpRequest, */
-                       HttpServletResponse theHttpResponse,
-                       @PathVariable("peerId") String thePeerId) {
+           HttpServletResponse theHttpResponse,
+           @PathVariable("peerId") String thePeerId) {
 
-               JsonResponse<List<MLPPeer>> response = new JsonResponse<List<MLPPeer>>();
                log.debug(API.Roots.LOCAL + "" + API.Paths.PEERS);
-               try {
-                       MLPPeer peer = this.peerService.getPeerById(thePeerId);
-                       response = this.clients.getFederationClient(peer.getApiUrl()).getPeers();
-
-                       theHttpResponse.setStatus(HttpServletResponse.SC_OK);
-               } 
-               catch (Exception x) {
-                       response = JsonResponse.<List<MLPPeer>> buildErrorResponse()
-                                                                                                                .withError(x)
-                                                                                                                .build();
-                       theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-                       log.error("Exception occurred during peer " + thePeerId + " getPeers", x);
-               }
-               return response;
+               return callPeer("getPeers", theHttpResponse, thePeerId, peer -> peer.getPeers());
        }
 
 }
index aa6350f..c66cd09 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -26,12 +26,9 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.acumos.cds.domain.MLPPeer;
 import org.acumos.federation.gateway.common.API;
-import org.acumos.federation.gateway.common.Clients;
 import org.acumos.federation.gateway.common.JsonResponse;
-import org.acumos.federation.gateway.service.PeerService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.CrossOrigin;
@@ -48,12 +45,6 @@ public class PeerPingController extends AbstractController {
 
        private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-       @Autowired
-       private Clients clients;
-       @Autowired
-       private PeerService peerService;
-       
-
        /**
         * Allows local components to ping a peer.
         * @param theHttpResponse
@@ -67,33 +58,9 @@ public class PeerPingController extends AbstractController {
        @RequestMapping(value = { API.Paths.PING }, method = RequestMethod.GET, produces = APPLICATION_JSON)
        @ResponseBody
        public JsonResponse<MLPPeer> ping(
-                       /* HttpServletRequest theHttpRequest, */
-                       HttpServletResponse theHttpResponse,
-                       @PathVariable("peerId") String thePeerId) {
-
-               JsonResponse<MLPPeer> response = new JsonResponse<MLPPeer>();
+           HttpServletResponse theHttpResponse,
+           @PathVariable("peerId") String thePeerId) {
                log.debug(API.Roots.LOCAL + "" + API.Paths.PING);
-               try {
-                       MLPPeer peer = this.peerService.getPeerById(thePeerId);
-                       if (peer == null) {
-                               response = JsonResponse.<MLPPeer> buildErrorResponse()
-                                                                                                                                .withMessage("No peer with id " + thePeerId + " found.")
-                                                                                                                                .build();
-                               theHttpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
-                       }
-                       else {
-                               response = this.clients.getFederationClient(peer.getApiUrl()).ping();
-                               theHttpResponse.setStatus(HttpServletResponse.SC_OK);
-                       }
-               } 
-               catch (Exception x) {
-                       response = JsonResponse.<MLPPeer> buildErrorResponse()
-                                                                                                                .withError(x)
-                                                                                                                .build();
-                       theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-                       log.error("Exception occurred during peer ping", x);
-               }
-               return response;
+               return callPeer("ping", theHttpResponse, thePeerId, peer -> peer.ping());
        }
-
 }
index 7e63103..ebd2124 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -26,12 +26,9 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.acumos.cds.domain.MLPPeer;
 import org.acumos.federation.gateway.common.API;
-import org.acumos.federation.gateway.common.Clients;
 import org.acumos.federation.gateway.common.JsonResponse;
-import org.acumos.federation.gateway.service.PeerService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.CrossOrigin;
@@ -48,12 +45,6 @@ public class PeerRegistrationController extends AbstractController {
 
        private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-       @Autowired
-       private Clients clients;
-       @Autowired
-       private PeerService peerService;
-       
-
        /**
         * Allows local components to require this Acumos instance to register with another Acumos instance.
         * @param theHttpResponse
@@ -67,26 +58,9 @@ public class PeerRegistrationController extends AbstractController {
        @RequestMapping(value = { API.Paths.PEER_REGISTER }, method = RequestMethod.POST, produces = APPLICATION_JSON)
        @ResponseBody
        public JsonResponse<MLPPeer> register(
-                       /* HttpServletRequest theHttpRequest, */
-                       HttpServletResponse theHttpResponse,
-                       @PathVariable("peerId") String thePeerId) {
-
-               JsonResponse<MLPPeer> response = new JsonResponse<MLPPeer>();
+           HttpServletResponse theHttpResponse,
+           @PathVariable("peerId") String thePeerId) {
                log.debug(API.Roots.LOCAL + "" + API.Paths.PEER_REGISTER);
-               try {
-                       MLPPeer peer = this.peerService.getPeerById(thePeerId);
-                       response = this.clients.getFederationClient(peer.getApiUrl()).register(peerService.getSelf());
-
-                       theHttpResponse.setStatus(HttpServletResponse.SC_OK);
-               } 
-               catch (Exception x) {
-                       response = JsonResponse.<MLPPeer> buildErrorResponse()
-                                                                                                                .withError(x)
-                                                                                                                .build();
-                       theHttpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-                       log.error("Exception occurred during peer " + thePeerId + " getPeers", x);
-               }
-               return response;
+               return callPeer("getPeers", theHttpResponse, thePeerId, peer -> peer.register(peerService.getSelf()) );
        }
-
 }
index e00d1bb..4b250e0 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -27,10 +27,12 @@ import java.util.List;
 import java.util.Map;
 
 import org.acumos.cds.domain.MLPArtifact;
+import org.acumos.cds.domain.MLPCatalog;
 import org.acumos.cds.domain.MLPDocument;
 import org.acumos.cds.domain.MLPSolution;
 import org.acumos.cds.domain.MLPSolutionRevision;
 import org.acumos.federation.gateway.cds.Artifact;
+import org.acumos.federation.gateway.cds.Catalog;
 import org.acumos.federation.gateway.cds.Document;
 import org.acumos.federation.gateway.cds.Solution;
 import org.acumos.federation.gateway.cds.SolutionRevision;
@@ -45,6 +47,29 @@ import org.acumos.federation.gateway.cds.SolutionRevision;
  */
 public interface CatalogService {
 
+       /**
+        * API to be invoked by Peer Acumos to fetch the list of visible Catalogs.
+        *
+        * @param theContext
+        *            the execution context.
+        *
+        * @return List of visible Catalogs.
+        *
+        * @throws ServiceException if an error is encountered during processing
+        */
+       public List<MLPCatalog> getCatalogs(ServiceContext theContext) throws ServiceException;
+
+       /**
+        * Default interface for calls in behalf of the local Acumos service.
+        *
+        * @return List of the visible Catalogs
+        *
+        * @throws ServiceException if an error is encoutered during processing
+        */
+       public default List<MLPCatalog> getCatalogs() throws ServiceException {
+               return getCatalogs(selfService());
+       }
+
        /**
         * API to be invoked by Peer Acumos to fetch the Catalog Solutions List.
         * 
index 8d506ce..601e3fc 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
 
 package org.acumos.federation.gateway.service.impl;
 
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.function.Consumer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.acumos.federation.gateway.cds.Mapper;
 import org.acumos.federation.gateway.security.Peer;
 import org.acumos.federation.gateway.service.LocalWatchService;
 import org.acumos.federation.gateway.service.ServiceContext;
@@ -28,8 +38,11 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationContext;
 import org.springframework.core.io.Resource;
 
+import com.fasterxml.jackson.databind.MappingIterator;
+import com.fasterxml.jackson.databind.ObjectReader;
 
 public class AbstractServiceLocalImpl {
+       private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
        protected Resource resource;
 
@@ -39,19 +52,35 @@ public class AbstractServiceLocalImpl {
        @Autowired
        protected LocalWatchService watcher;
 
-       public void setSource(String theSource) {
-               this.resource = this.appCtx.getResource(theSource);
+       protected synchronized <T> void reload(Class<T> clazz, Resource resource, Consumer<List<T>> setter, String label) {
+               try {
+                       ObjectReader objectReader = Mapper.build().reader(clazz);
+                       MappingIterator objectIterator = objectReader.readValues(resource.getURL());
+                       List<T> values = objectIterator.readAll();
+                       setter.accept(values);
+                       log.info("loaded {} {}", values.size(), label);
+               } catch (Exception x) {
+                       throw new BeanInitializationException("Failed to load " + label + " from " + resource, x);
+               }
        }
 
-       protected void checkResource() {
-
-               if (this.resource == null) {
-                       throw new BeanInitializationException("No source was configured");
+       protected <T> void monitor(Class<T> clazz, Resource resource, Consumer<List<T>> setter, String label) {
+               if (resource == null) {
+                       throw new BeanInitializationException("No source for " + label + " was configured");
                }
-
-               if (!this.resource.exists()) {
-                       throw new BeanInitializationException("Source " + this.resource + " does not exist");
+               if (!resource.exists()) {
+                       throw new BeanInitializationException("Source " + resource + " for " + label + " does not exist");
+               }
+               try {
+                       watcher.watchOn(resource.getURL().toURI(), (uri) -> reload(clazz, resource, setter, label));
+               } catch (IOException | URISyntaxException iox) {
+                       log.info("Failed to register {} watcher for {}", label, resource);
                }
+               reload(clazz, resource, setter, label);
+       }
+
+       public void setSource(String theSource) {
+               this.resource = this.appCtx.getResource(theSource);
        }
 
        public ServiceContext selfService() {
index 9f01fe1..04c7b27 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -29,6 +29,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -41,6 +42,7 @@ import javax.annotation.PostConstruct;
 import org.acumos.cds.AccessTypeCode;
 import org.acumos.cds.client.ICommonDataServiceRestClient;
 import org.acumos.cds.domain.MLPArtifact;
+import org.acumos.cds.domain.MLPCatalog;
 import org.acumos.cds.domain.MLPDocument;
 import org.acumos.cds.domain.MLPSolution;
 import org.acumos.cds.domain.MLPSolutionRevision;
@@ -49,6 +51,7 @@ import org.acumos.cds.transport.RestPageRequest;
 import org.acumos.cds.transport.RestPageResponse;
 import org.acumos.federation.gateway.cds.AccessType;
 import org.acumos.federation.gateway.cds.Artifact;
+import org.acumos.federation.gateway.cds.Catalog;
 import org.acumos.federation.gateway.cds.Document;
 import org.acumos.federation.gateway.cds.Solution;
 import org.acumos.federation.gateway.cds.SolutionRevision;
@@ -90,6 +93,27 @@ public class CatalogServiceImpl extends AbstractServiceImpl
        public void initService() {
        }
 
+       @Override
+       public List<MLPCatalog> getCatalogs(ServiceContext theContext) throws ServiceException {
+               log.debug("getCatalogs");
+               ICommonDataServiceRestClient cdsClient = getClient(theContext);
+               Set<String> atcs = new HashSet<String>(Arrays.asList(atcArray(config.getSolutionsSelector())));
+               List<MLPCatalog> catalogs = allPages("getCatalogs", page -> cdsClient.getCatalogs(page))
+                   .stream().filter(mcat -> atcs.contains(mcat.getAccessTypeCode()))
+                   .collect(Collectors.toList());
+               for (MLPCatalog mcat: catalogs) {
+                       try {
+                               // Don't have getCatalogSolutionCount() in cds 2.1
+                               //((Catalog)mcat).setSize((int)cdsClient.getCatalogSolutionCount(mcat.getCatalogId()));
+                               ((Catalog)mcat).setSize(getSolutions(Collections.singletonMap(Solution.Fields.catalogId, mcat.getCatalogId()), theContext).size());
+                       } catch (HttpStatusCodeException ex) {
+                               throw new ServiceException("Failed to count solutions in catalog", ex);
+                       }
+               }
+               log.debug("getCatalogs: catalogs count {}", catalogs.size());
+               return catalogs;
+       }
+
        @Override
        public List<MLPSolution> getSolutions(Map<String, ?> theSelector, ServiceContext theContext) throws ServiceException {
                log.debug("getSolutions with selector {}", theSelector);
@@ -101,55 +125,20 @@ public class CatalogServiceImpl extends AbstractServiceImpl
                //could overwrite the basic one end expose non public solutions ..).
                selector.putAll(this.config.getSolutionsSelector());
                log.debug("getSolutions with full selector {}", selector);
-
-               RestPageRequest pageRequest = new RestPageRequest(0, this.cdsConfig.getPageSize());
-               RestPageResponse<MLPSolution> pageResponse = null;
-               List<MLPSolution> solutions = new ArrayList<MLPSolution>(),
-                                                                                               pageSolutions = null;
                ICommonDataServiceRestClient cdsClient = getClient(theContext);
-               try {
-                       Predicate<MLPSolution> matcher = ServiceImpl.compileSelector(selector);
-                       String catid = (String)selector.get(Solution.Fields.catalogId);
-                       Function<RestPageRequest, RestPageResponse<MLPSolution>> pager = null;
-                       if (catid != null) {
-                               pager = page -> cdsClient.getSolutionsInCatalog(catid, page);
-                       } else {
-                               boolean active = (Boolean)selector.getOrDefault(Solution.Fields.active, Boolean.TRUE);
-                               Object o = selector.getOrDefault(Solution.Fields.accessTypeCode, allATs);
-                               String[] codes = null;
-                               if (o instanceof String) {
-                                       codes = new String[] { (String)o };
-                               } else {
-                                       codes = ((List<String>)o).toArray(new String[0]);
-                               }
-                               String[] xcodes = codes;
-                               Instant since = Instant.ofEpochSecond(((Number)selector.get(Solution.Fields.modified)).longValue());
-                               pager = page -> cdsClient.findSolutionsByDate(active, xcodes, since, page);
-                       }
-                       do {
-                               log.debug("getSolutions page {}", pageResponse);
-                               pageResponse = pager.apply(pageRequest);
-                       
-                               log.debug("getSolutions page response {}", pageResponse);
-                               //we need to post-process all other selection criteria
-                               pageSolutions = pageResponse.getContent().stream()
-                                                                                                                       .filter(matcher)
-                                                                                                                       .collect(Collectors.toList());
-                               log.debug("getSolutions page selection {}", pageSolutions);
-               
-                               pageRequest.setPage(pageResponse.getNumber() + 1);
-                               solutions.addAll(pageSolutions);
-                       } while (!pageResponse.isLast());
-               }
-               catch (HttpStatusCodeException restx) {
-                       if (Errors.isCDSNotFound(restx))
-                               return Collections.EMPTY_LIST;
-                       else {
-                               log.debug("getSolutions failed {}: {}", restx, restx.getResponseBodyAsString());
-                               throw new ServiceException("Failed to retrieve solutions", restx);
-                       }
+               Predicate<MLPSolution> matcher = ServiceImpl.compileSelector(selector);
+               String catid = (String)selector.get(Solution.Fields.catalogId);
+               Function<RestPageRequest, RestPageResponse<MLPSolution>> pager = null;
+               if (catid != null) {
+                       pager = page -> cdsClient.getSolutionsInCatalog(catid, page);
+               } else {
+                       boolean active = (Boolean)selector.getOrDefault(Solution.Fields.active, Boolean.TRUE);
+                       String[] codes = atcArray(selector);
+                       Instant since = Instant.ofEpochSecond(((Number)selector.get(Solution.Fields.modified)).longValue());
+                       pager = page -> cdsClient.findSolutionsByDate(active, codes, since, page);
                }
-
+               List<MLPSolution> solutions = allPages("getSolutions", pager)
+                   .stream().filter(matcher).collect(Collectors.toList());
                log.debug("getSolutions: solutions count {}", solutions.size());
                return solutions;
        }
@@ -379,4 +368,32 @@ public class CatalogServiceImpl extends AbstractServiceImpl
                }
        }
 
+       private static String[] atcArray(Map<String, Object> selector) {
+               Object o = selector.getOrDefault(Solution.Fields.accessTypeCode, allATs);
+               return (o instanceof String)? new String[] { (String)o }: ((List<String>)o).toArray(new String[0]);
+       }
+
+       private <T> List<T> allPages(String opname, Function<RestPageRequest, RestPageResponse<T>> fcn) throws ServiceException {
+               RestPageRequest request = new RestPageRequest(0, cdsConfig.getPageSize());
+               List<T> ret = new ArrayList<T>();
+               while (true) {
+                       try {
+                               RestPageResponse<T> response = fcn.apply(request);
+                               List<T> returned = response.getContent();
+                               log.debug("{} returned {}", opname, returned);
+                               request.setPage(response.getNumber() + 1);
+                               ret.addAll(returned);
+                               if (response.isLast()) {
+                                       break;
+                               }
+                       } catch (HttpStatusCodeException restx) {
+                               if (Errors.isCDSNotFound(restx)) {
+                                       break;
+                               }
+                               log.debug("{} failed {}: {}", opname, restx, restx.getResponseBodyAsString());
+                               throw new ServiceException("Failed on " + opname, restx);
+                       }
+               }
+               return ret;
+       }
 }
index 75bd770..9c19e6a 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -20,9 +20,7 @@
 
 package org.acumos.federation.gateway.service.impl;
 
-import java.io.IOException;
 import java.lang.invoke.MethodHandles;
-import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -34,12 +32,13 @@ import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 
 import org.acumos.cds.domain.MLPArtifact;
+import org.acumos.cds.domain.MLPCatalog;
 import org.acumos.cds.domain.MLPDocument;
 import org.acumos.cds.domain.MLPSolution;
 import org.acumos.cds.domain.MLPSolutionRevision;
 import org.acumos.federation.gateway.cds.Artifact;
+import org.acumos.federation.gateway.cds.Catalog;
 import org.acumos.federation.gateway.cds.Document;
-import org.acumos.federation.gateway.cds.Mapper;
 import org.acumos.federation.gateway.cds.Solution;
 import org.acumos.federation.gateway.cds.SolutionRevision;
 import org.acumos.federation.gateway.service.CatalogService;
@@ -49,10 +48,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.BeanInitializationException;
 import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.core.io.Resource;
 import org.springframework.stereotype.Service;
 
-import com.fasterxml.jackson.databind.MappingIterator;
-import com.fasterxml.jackson.databind.ObjectReader;
 
 /**
  * 
@@ -64,23 +62,18 @@ public class CatalogServiceLocalImpl extends AbstractServiceLocalImpl implements
 
        private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
+       private Resource catalogresource;
+       private List<Catalog> catalogs;
        private List<Solution> solutions;
 
+       public void setCatalogs(String theCatalogs) {
+               this.catalogresource = appCtx.getResource(theCatalogs);
+       }
+
        @PostConstruct
        public void initService() {
-
-               checkResource();
-               try {
-                       watcher.watchOn(this.resource.getURL().toURI(), (uri) -> {
-                               loadSolutionsCatalogInfo();
-                       });
-
-               } catch (IOException | URISyntaxException iox) {
-                       log.info("Catalog watcher registration failed for " + this.resource, iox);
-               }
-
-               loadSolutionsCatalogInfo();
-
+               monitor(Solution.class, resource, (solns) -> this.solutions = solns, "solutions");
+               monitor(Catalog.class, catalogresource, (cats) -> this.catalogs = cats, "catalogs");
                // Done
                log.debug("Local CatalogService available");
        }
@@ -89,19 +82,10 @@ public class CatalogServiceLocalImpl extends AbstractServiceLocalImpl implements
        public void cleanupService() {
        }
 
-       /** */
-       private void loadSolutionsCatalogInfo() {
-               synchronized (this) {
-                       try {
-                               ObjectReader objectReader = Mapper.build()
-                                                                                                                                                       .reader(Solution.class);
-                               MappingIterator objectIterator = objectReader.readValues(this.resource.getURL());
-                               this.solutions = objectIterator.readAll();
-                               log.info("loaded " + this.solutions.size() + " solutions");
-                       } catch (Exception x) {
-                               throw new BeanInitializationException("Failed to load solutions catalog from " + this.resource, x);
-                       }
-               }
+       @Override
+       public List<MLPCatalog> getCatalogs(ServiceContext theContext) throws ServiceException {
+               log.debug("getCatalogs()");
+               return new ArrayList<MLPCatalog>(catalogs);
        }
 
        @Override
index 4d910a1..658c059 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -26,6 +26,7 @@ import java.net.URISyntaxException;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Consumer;
 
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
@@ -38,6 +39,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.BeanInitializationException;
 import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.core.io.Resource;
 import org.springframework.stereotype.Service;
 
 import com.fasterxml.jackson.core.type.TypeReference;
@@ -58,19 +60,7 @@ public class CodeNamesServiceLocalImpl extends AbstractServiceLocalImpl implemen
 
        @PostConstruct
        public void initService() {
-
-               checkResource();
-               try {
-                       watcher.watchOn(this.resource.getURL().toURI(), (uri) -> {
-                               loadCodeNamesInfo();
-                       });
-
-               } catch (IOException | URISyntaxException iox) {
-                       log.info("Code names watcher registration failed for " + this.resource, iox);
-               }
-
-               loadCodeNamesInfo();
-
+               monitor(Object.class, resource, null, "codes");
                // Done
                log.debug("Local CodeNamesService available");
        }
@@ -79,17 +69,16 @@ public class CodeNamesServiceLocalImpl extends AbstractServiceLocalImpl implemen
        public void cleanupService() {
        }
 
-       /** */
-       private void loadCodeNamesInfo() {
+       @Override
+       protected synchronized <T> void reload(Class<T> clazz, Resource resource, Consumer<List<T>> setter, String label) {
                synchronized (this) {
                        try {
-                               ObjectMapper mapper = new ObjectMapper()
-                                                                                                                                                       .registerModule(new JavaTimeModule());
+                               ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
                                this.codes = mapper.readValue(this.resource.getURL(), new TypeReference<Map<CodeNameType, List<MLPCodeNamePair>>>(){});
-                               log.info("loaded " + this.codes.size() + " codes");
+                               log.info("loaded {} {}", this.codes.size(), label);
                        }
                        catch (Exception x) {
-                               throw new BeanInitializationException("Failed to load codes from " + this.resource, x);
+                               throw new BeanInitializationException("Failed to load " + label + " from " + resource, x);
                        }
                }
        }
index e190a84..3b9cc0c 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -60,38 +60,11 @@ public class PeerServiceLocalImpl extends AbstractServiceLocalImpl implements Pe
        @PostConstruct
        public void initPeerService() {
                log.debug("init local peer info service");
-               checkResource();
-               try {
-                       watcher.watchOn(this.resource.getURL().toURI(), (uri) -> {
-                               loadPeersSubscriptionsInfo();
-                       });
-               } catch (IOException | URISyntaxException iox) {
-                       log.info("Peers subscriptions watcher registration failed for " + this.resource, iox);
-               }
-
-               loadPeersSubscriptionsInfo();
-
+               monitor(FLPPeer.class, resource, (prs) -> this.peers = prs, "peers");
                // Done
                log.debug("Local PeerService available");
        }
 
-       private void loadPeersSubscriptionsInfo() {
-               log.info("Loading peers subscriptions from " + this.resource);
-               synchronized (this) {
-                       try {
-                               ObjectReader objectReader = new ObjectMapper()
-                                                                                                                                                                       .registerModule(new JavaTimeModule())
-                                                                                                                                                                       .reader(FLPPeer.class);
-                               MappingIterator objectIterator = objectReader.readValues(this.resource.getURL());
-                               this.peers = objectIterator.readAll();
-                               log.info("loaded " + this.peers.size() + " peers");
-                       }
-                       catch (Exception x) {
-                               throw new BeanInitializationException("Failed to load solutions catalog from " + this.resource, x);
-                       }
-               }
-       }
-
        @PreDestroy
        public void cleanupPeerService() {
                log.debug("Local peer info service destroyed");
index 936c57b..25add47 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -76,6 +76,7 @@ import org.springframework.test.context.junit4.SpringRunner;
                                                                        "codes-local.source=classpath:test-codes.json",
                                                                        "peers-local.source=classpath:test-peers.json",
                                                                        "catalog-local.source=classpath:test-catalog.json",
+                                                                       "catalog-local.catalogs=classpath:test-catalogs.json",
                                                                        "federation.ssl.key-store=classpath:acumosa.pkcs12",
                                                                        "federation.ssl.key-store-password=acumosa",
                                                                        "federation.ssl.key-store-type=PKCS12",
index e078ef7..6da13c9 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.acumos.federation.gateway.test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -29,10 +31,12 @@ import java.util.Map;
 import java.util.List;
 
 import org.acumos.cds.domain.MLPArtifact;
+import org.acumos.cds.domain.MLPCatalog;
 import org.acumos.cds.domain.MLPPeer;
 import org.acumos.cds.domain.MLPSolution;
 import org.acumos.cds.domain.MLPSolutionRevision;
 import org.acumos.federation.gateway.cds.Artifact;
+import org.acumos.federation.gateway.cds.Catalog;
 import org.acumos.federation.gateway.cds.Document;
 import org.acumos.federation.gateway.cds.Solution;
 import org.acumos.federation.gateway.cds.SolutionRevision;
@@ -97,6 +101,9 @@ public class CatalogServiceTest extends ServiceTest {
 
        protected void initMockResponses() {
 
+               registerMockResponse("GET /ccds/catalog?page=0&size=100", MockResponse.success("mockCDSPortalCatalogsResponse.json"));
+               registerMockResponse("GET /ccds/catalog/5ebbc521-1642-4d4c-a732-d9e8a6b51f4a/solution?page=0&size=100", MockResponse.success("mockCDSPortalSolutionsResponse.json"));
+               registerMockResponse("GET /ccds/catalog/e072d118-0875-438b-8c9e-cf1f8ef3d9cb/solution?page=0&size=100", MockResponse.success("mockCDSPortalSolutionsResponse.json"));
                registerMockResponse("GET /ccds/catalog/mycatalog/solution?page=0&size=100", MockResponse.success("mockCDSPortalSolutionsResponse.json"));
                registerMockResponse("GET /ccds/solution/search/date?atc=PB&inst=1000&active=true&page=0&size=100", MockResponse.success("mockCDSPortalSolutionsResponse.json"));
                registerMockResponse("GET /ccds/solution/search/date?atc=PB&inst=1531747662000&active=true&page=0&size=100", MockResponse.success("mockCDSDateSolutionsResponsePage0.json"));
@@ -132,84 +139,86 @@ public class CatalogServiceTest extends ServiceTest {
                        ServiceContext selfService = 
                                ServiceContext.forPeer(new Peer(new MLPPeer("acumosa", "gateway.acumosa.org", "https://gateway.acumosa.org:9084", false, false, "admin@acumosa.org", "AC"), Role.SELF));
 
+                       List<MLPCatalog> catalogs = catalog.getCatalogs(selfService);
+                       assertEquals("Unexpected catalogs count", 2, catalogs.size());
+                       assertEquals("Unexpected catalog solution count", 5, ((Catalog)catalogs.get(0)).getSize());
                        List<MLPSolution> solutions = catalog.getSolutions(selector("catalogId", "mycatalog"), selfService);
-                       assertTrue("Unexpected solutions count: " + solutions.size(), solutions.size() == 5);
+                       assertEquals("Unexpected solutions count", 5, solutions.size());
                        solutions = catalog.getSolutions(selector("catalogId", "mycatalog", "modelTypeCode", "RG"), selfService);
-                       assertTrue("Unexpected solutions count: " + solutions.size(), solutions.size() == 2);
+                       assertEquals("Unexpected solutions count", 2, solutions.size());
                        solutions = catalog.getSolutions(selector("catalogId", "mycatalog", "toolkitTypeCode", new CopyOnWriteArrayList(new String[] {"CP", "TF" })), selfService);
-                       assertTrue("Unexpected solutions count: " + solutions.size(), solutions.size() == 3);
+                       assertEquals("Unexpected solutions count", 3, solutions.size());
                        solutions = catalog.getSolutions(selector("catalogId", "mycatalog", "tags", "subtract"), selfService);
-                       assertTrue("Unexpected solutions count: " + solutions.size(), solutions.size() == 1);
+                       assertEquals("Unexpected solutions count", 1, solutions.size());
                        solutions = catalog.getSolutions(selector("catalogId", "mycatalog", "tags", new CopyOnWriteArrayList(new String[] { "subtract", "poutput"})), selfService);
-                       assertTrue("Unexpected solutions count: " + solutions.size(), solutions.size() == 2);
+                       assertEquals("Unexpected solutions count", 2, solutions.size());
                        solutions = catalog.getSolutions(selector("catalogId", "mycatalog", "solutionId", "38efeef1-e4f4-4298-9f68-6f0052d6ade9"), selfService);
-                       assertTrue("Unexpected solutions count: " + solutions.size(), solutions.size() == 1);
+                       assertEquals("Unexpected solutions count", 1, solutions.size());
                        SolutionRevision rev = catalog.getSolutionRevision("38efeef1-e4f4-4298-9f68-6f0052d6ade9", "2c7e4481-6e6f-47d9-b7a4-c4e674d2b341");
                        Artifact art = catalog.getSolutionRevisionArtifact("2c2c2c2c-6e6f-47d9-b7a4-c4e674d2b341");
                        Document doc = catalog.getSolutionRevisionDocument("2c2c2c2c-6e6f-47d9-b7a4-c4e674d2b342");
                        try {
                                catalog.getSolutions(selector("catalogId", "mycatalog", "name", new CopyOnWriteArrayList(new String[] { "A", "B" })), selfService);
-                               assertTrue("Expected service exception, got none", 1 == 0);
+                               fail("Expected service exception, got none");
                        }
                        catch (ServiceException sx) {
                        }
                        try {
                                catalog.getSolutions(selector("catalogId", "mycatalog", "name", true), selfService);
-                               assertTrue("Expected service exception, got none", 1 == 0);
+                               fail("Expected service exception, got none");
                        }
                        catch (ServiceException sx) {
                        }
                        try {
                                catalog.getSolutions(selector("catalogId", "mycatalog", "tags", true), selfService);
-                               assertTrue("Expected service exception, got none", 1 == 0);
+                               fail("Expected service exception, got none");
                        }
                        catch (ServiceException sx) {
                        }
                        solutions = catalog.getSolutions(Collections.EMPTY_MAP, selfService);
-                       assertTrue("Unexpected solutions count: " + solutions.size(), solutions.size() == 5);
+                       assertEquals("Unexpected solutions count", 5, solutions.size());
                
                        Solution solution = catalog.getSolution("10101010-1010-1010-1010-101010101010", selfService);
-                       assertTrue("Unexpected solution info", solution.getName().equals("test"));
+                       assertEquals("Unexpected solution info", "test", solution.getName());
 
                        List<? extends MLPSolutionRevision> revisions = solution.getRevisions();
                        if (revisions != null && !revisions.isEmpty()) {
-                               assertTrue("Unexpected revisions count: " + revisions.size(), revisions.size() == 1);
+                               assertEquals("Unexpected revisions count", 1, revisions.size());
                                for (MLPSolutionRevision revision: revisions) {
                                        assertTrue("Unexpected revision info", revision.getVersion().startsWith("1"));
                                        List<MLPArtifact> artifacts = catalog.getSolutionRevisionArtifacts(solution.getSolutionId(), revision.getRevisionId());
-                                       assertTrue("Unexpected artifacts count: " + artifacts.size(), artifacts.size() == 1);
-                                               //catalog.getSolutionRevisionArtifact();
+                                       assertEquals("Unexpected artifacts count", 1, artifacts.size());
                                }
                        }
 
                        //no such entry
                        Solution nsSolution = catalog.getSolution("f0f0f0f0-f0f0-f0f0-f0f0-f0f0f0f0f0f0", selfService);
-                       assertTrue("Unexpected no such solution outcome", nsSolution == null);
+                       assertNull("Unexpected no such solution outcome", nsSolution);
 
                        List<MLPSolutionRevision> nsRevisions = catalog.getSolutionRevisions("f0f0f0f0-f0f0-f0f0-f0f0-f0f0f0f0f0f0", selfService);
-                       assertTrue("Unexpected no such solution (revisions) outcome", nsRevisions == null);
+                       assertNull("Unexpected no such solution (revisions) outcome", nsRevisions);
 
                        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);
+                       assertNull("Unexpected no such solution (revision artifacts) outcome", nsArtifacts);
 
                        //other errors
                        try {
                                catalog.getSolution("f1f1f1f1-f1f1-f1f1-f1f1-f1f1f1f1f1f1", selfService);
-                               assertTrue("Expected service exception, got none", 1 == 0);
+                               fail("Expected service exception, got none");
                        }
                        catch (ServiceException sx) {
                        }
                
                        try {
                                catalog.getSolutionRevisions("f1f1f1f1-f1f1-f1f1-f1f1-f1f1f1f1f1f1", selfService);
-                               assertTrue("Expected service exception, got none", 1 == 0);
+                               fail("Expected service exception, got none");
                        }
                        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);
+                               fail("Expected service exception, got none");
                        }
                        catch (ServiceException sx) {
                        }
@@ -218,5 +227,4 @@ public class CatalogServiceTest extends ServiceTest {
                        fail("Unexpected catalog test outcome: " + org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(x));
                }
        }
-
 }
index 5d906a5..fde890d 100644 (file)
@@ -85,6 +85,7 @@ import org.acumos.federation.gateway.cds.Document;
        "codes-local.source=classpath:test-codes.json",
        "peers-local.source=classpath:test-peers.json",
        "catalog-local.source=classpath:test-catalog.json",
+       "catalog-local.catalogs=classpath:test-catalogs.json",
        "federation.ssl.key-store=classpath:acumosa.pkcs12",
        "federation.ssl.key-store-password=acumosa",
        "federation.ssl.key-store-type=PKCS12",
index 56d5c08..48339ed 100644 (file)
@@ -33,6 +33,7 @@ import org.acumos.cds.domain.MLPDocument;
 import org.acumos.cds.domain.MLPPeer;
 import org.acumos.cds.domain.MLPSolution;
 import org.acumos.cds.domain.MLPSolutionRevision;
+import org.acumos.federation.gateway.cds.Catalog;
 import org.acumos.federation.gateway.common.JsonResponse;
 import org.acumos.federation.gateway.config.InterfaceConfigurationBuilder;
 import org.acumos.federation.gateway.config.InterfaceConfigurationBuilder.SSLBuilder;
@@ -77,6 +78,7 @@ import org.springframework.test.context.junit4.SpringRunner;
                                                                        "codes-local.source=classpath:test-codes.json",
                                                                        "peers-local.source=classpath:test-peers.json",
                                                                        "catalog-local.source=classpath:test-catalog.json",
+                                                                       "catalog-local.catalogs=classpath:test-catalogs.json",
                                                                        "federation.ssl.key-store=classpath:acumosa.pkcs12",
                                                                        "federation.ssl.key-store-password=acumosa",
                                                                        "federation.ssl.key-store-type=PKCS12",
@@ -138,6 +140,15 @@ public class ControllerTest {
                assertGoodResponse("testPing", response);
        }
 
+       @Test
+       public void testCatalogs() {
+               setClient(PEERID);
+
+               ResponseEntity<JsonResponse<List<Catalog>>> response =
+                       this.restTemplate.exchange("https://localhost:" + this.port + "/catalogs", HttpMethod.GET, prepareRequest(), new ParameterizedTypeReference<JsonResponse<List<Catalog>>>() {});
+               
+               assertGoodResponseWith("testCatalogs", response, content -> content.size() == 2 && ((Catalog)content.get(0)).getSize() == 2);
+       }
        @Test
        public void testSolutions() {
                setClient(PEERID);
index 39fa258..426f549 100644 (file)
@@ -32,6 +32,7 @@ import java.util.List;
 import java.util.HashMap;
 import java.util.Scanner;
 
+import org.acumos.cds.domain.MLPCatalog;
 import org.acumos.cds.domain.MLPPeer;
 import org.acumos.cds.domain.MLPSolution;
 import org.acumos.federation.gateway.common.API;
@@ -108,6 +109,7 @@ import org.springframework.web.client.RestTemplate;
                                                                        "codes-local.source=classpath:test-codes.json",
                                                                        "peers-local.source=classpath:test-peers.json",
                                                                        "catalog-local.source=classpath:test-catalog.json",
+                                                                       "catalog-local.catalogs=classpath:test-catalogs.json",
                                                                        "federation.server.port=${random.int[49152,65535]}",
                                                                        "federation.ssl.key-store=classpath:acumosa.pkcs12",
                                                                        "federation.ssl.key-store-password=acumosa",
@@ -165,6 +167,7 @@ public class LocalControllerTest {
                MockitoAnnotations.initMocks(this);
 
                peerAnswer
+                       .mockResponse(info -> info.getPath().equals("/catalogs"), MockResponse.success("mockPeerCatalogsResponse.json"))
                        .mockResponse(info -> info.getPath().equals("/solutions"), MockResponse.success("mockPeerSolutionsResponse.json"))
                        .mockResponse(info -> info.getPath().contains("/solutions/") && !info.getPath().contains("/revisions"), MockResponse.success("mockPeerSolutionResponse.json"))
                        .mockResponse(info -> info.getPath().endsWith("/revisions"), MockResponse.success("mockPeerSolutionRevisionsResponse.json"))
@@ -224,6 +227,17 @@ public class LocalControllerTest {
                }
        }
 
+       @Test
+       public void testPeerCatalogs() {
+               String url = "https://localhost:" + this.localPort + "/peer/11111111-1111-1111-1111-111111111111/catalogs";
+
+               log.info("testPeerCatalogs: {}", url);
+               ResponseEntity<JsonResponse<List<MLPCatalog>>> response =
+                       this.restTemplate.exchange(url, HttpMethod.GET, prepareRequest(), new ParameterizedTypeReference<JsonResponse<List<MLPCatalog>>>() {});
+               
+               assertGoodResponseWith("testPeerCatalogs", response, content -> content.size() == 2);
+       }
+
        @Test
        public void testPeerSolutions() {
                String url = "https://localhost:" + this.localPort + "/peer/11111111-1111-1111-1111-111111111111/solutions";
@@ -295,9 +309,9 @@ public class LocalControllerTest {
                String badpeer = "https://localhost:" + this.localPort + "/peer/11111111-1111-1111-1111-111111111112";
        
                verifyFail(badpeer + "/ping", HttpMethod.GET, new ParameterizedTypeReference<JsonResponse<MLPPeer>>() {}, 404);
-               verifyFail(badpeer + "/peers", HttpMethod.GET, new ParameterizedTypeReference<JsonResponse<List<MLPPeer>>>() {}, 500);
-               verifyFail(badpeer + "/solutions?selector=e30K", HttpMethod.GET, new ParameterizedTypeReference<JsonResponse<List<MLPSolution>>>() {}, 500);
-               verifyFail(badpeer + "/solutions/00000000-0000-0000-0000-000000000000", HttpMethod.GET, new ParameterizedTypeReference<JsonResponse<MLPSolution>>() {}, 500);
+               verifyFail(badpeer + "/peers", HttpMethod.GET, new ParameterizedTypeReference<JsonResponse<List<MLPPeer>>>() {}, 404);
+               verifyFail(badpeer + "/solutions?selector=e30K", HttpMethod.GET, new ParameterizedTypeReference<JsonResponse<List<MLPSolution>>>() {}, 404);
+               verifyFail(badpeer + "/solutions/00000000-0000-0000-0000-000000000000", HttpMethod.GET, new ParameterizedTypeReference<JsonResponse<MLPSolution>>() {}, 404);
        }
 
        @Test
index bd33f75..8d08ecf 100644 (file)
@@ -2,7 +2,7 @@
  * ===============LICENSE_START=======================================================
  * Acumos
  * ===================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
+ * Copyright (C) 2017-2019 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");
@@ -69,7 +69,8 @@ import org.springframework.test.context.junit4.SpringRunner;
                                                                        "federation.ssl.client-auth=need",
                                                                        "codes-local.source=classpath:/test-codes.json",
                                                                        "peers-local.source=classpath:/task-test-peers.json",
-                                                                       "catalog-local.source=classpath:/task-test-catalog.json"
+                                                                       "catalog-local.source=classpath:/task-test-catalog.json",
+                                                                       "catalog-local.catalogs=classpath:test-catalogs.json"
                                                                })
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class TaskTest {
diff --git a/gateway/src/test/resources/mockCDSPortalCatalogsResponse.json b/gateway/src/test/resources/mockCDSPortalCatalogsResponse.json
new file mode 100644 (file)
index 0000000..ee339e4
--- /dev/null
@@ -0,0 +1,45 @@
+{
+       "content": [
+               {
+                       "catalogId": "hidden21-1642-4d4c-a732-d9e8a6b51f4a",
+                       "accessTypeCode": "PR",
+                       "name": "Hidden Catalog",
+                       "publisher": "Acumos",
+                       "description": "Catalog to never return",
+                       "origin": null,
+                       "url": "https://www.example.org",
+                       "created": "2019-03-20T20:17:01Z",
+                       "modified": "2019-03-20T20:17:01Z"
+               },
+               {
+                       "catalogId": "5ebbc521-1642-4d4c-a732-d9e8a6b51f4a",
+                       "accessTypeCode": "PB",
+                       "name": "Test Catalog 1",
+                       "publisher": "Acumos",
+                       "description": "First catalog for unit testing",
+                       "origin": null,
+                       "url": "https://www.example.org",
+                       "created": "2019-03-20T20:17:01Z",
+                       "modified": "2019-03-20T20:17:01Z"
+               },
+               {
+                       "catalogId": "e072d118-0875-438b-8c9e-cf1f8ef3d9cb",
+                       "accessTypeCode": "PB",
+                       "name": "Test Catalog 2",
+                       "publisher": "Acumos",
+                       "description": "Second catalog for unit testing",
+                       "origin": null,
+                       "url": "https://www.example.org",
+                       "created": "2019-03-20T20:18:01Z",
+                       "modified": "2019-03-20T20:18:01Z"
+               }
+       ],
+       "last": true,
+       "totalPages": 1,
+       "totalElements": 2,
+       "sort": null,
+       "first": true,
+       "numberOfElements": 2,
+       "size": 100,
+       "number": 0
+}
diff --git a/gateway/src/test/resources/mockPeerCatalogsResponse.json b/gateway/src/test/resources/mockPeerCatalogsResponse.json
new file mode 100644 (file)
index 0000000..9192062
--- /dev/null
@@ -0,0 +1,37 @@
+{
+       "message": "success",
+       "content": [
+               {
+                       "catalogId": "5ebbc521-1642-4d4c-a732-d9e8a6b51f4a",
+                       "accessTypeCode": "PB",
+                       "name": "Test Catalog 1",
+                       "publisher": "Acumos",
+                       "description": "First catalog for unit testing",
+                       "origin": null,
+                       "url": "https://www.example.org",
+                       "size": 99,
+                       "created": "2019-03-20T20:17:01Z",
+                       "modified": "2019-03-20T20:17:01Z"
+               },
+               {
+                       "catalogId": "e072d118-0875-438b-8c9e-cf1f8ef3d9cb",
+                       "accessTypeCode": "PB",
+                       "name": "Test Catalog 2",
+                       "publisher": "Acumos",
+                       "description": "Second catalog for unit testing",
+                       "origin": null,
+                       "url": "https://www.example.org",
+                       "size": 55,
+                       "created": "2019-03-20T20:18:01Z",
+                       "modified": "2019-03-20T20:18:01Z"
+               }
+       ],
+       "last": true,
+       "totalPages": 1,
+       "totalElements": 2,
+       "sort": null,
+       "first": true,
+       "numberOfElements": 2,
+       "size": 100,
+       "number": 0
+}
diff --git a/gateway/src/test/resources/test-catalogs.json b/gateway/src/test/resources/test-catalogs.json
new file mode 100644 (file)
index 0000000..826937f
--- /dev/null
@@ -0,0 +1,27 @@
+[
+       {
+               "catalogId": "5ebbc521-1642-4d4c-a732-d9e8a6b51f4a",
+               "accessTypeCode": "PB",
+               "name": "Test Catalog 1",
+               "publisher": "Acumos",
+               "description": "First catalog for unit testing",
+               "origin": null,
+               "url": "https://www.example.org",
+               "size": 2,
+               "created": "2019-03-20T20:17:01Z",
+               "modified": "2019-03-20T20:17:01Z"
+       },
+       {
+               "catalogId": "e072d118-0875-438b-8c9e-cf1f8ef3d9cb",
+               "accessTypeCode": "PB",
+               "name": "Test Catalog 2",
+               "publisher": "Acumos",
+               "description": "Second catalog for unit testing",
+               "origin": null,
+               "url": "https://www.example.org",
+               "size": 2,
+               "created": "2019-03-20T20:18:01Z",
+               "modified": "2019-03-20T20:18:01Z"
+       }
+
+]