Add catalogId field to catalog search selector
[federation.git] / gateway / src / main / java / org / acumos / federation / gateway / service / impl / CatalogServiceImpl.java
1 /*-
2  * ===============LICENSE_START=======================================================
3  * Acumos
4  * ===================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
6  * ===================================================================================
7  * This Acumos software file is distributed by AT&T and Tech Mahindra
8  * under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *  
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *  
14  * This file is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ===============LICENSE_END=========================================================
19  */
20
21 /**
22  * 
23  */
24 package org.acumos.federation.gateway.service.impl;
25
26 import java.lang.invoke.MethodHandles;
27 import java.time.Instant;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.function.Predicate;
36 import java.util.function.Function;
37 import java.util.stream.Collectors;
38
39 import javax.annotation.PostConstruct;
40
41 import org.acumos.cds.AccessTypeCode;
42 import org.acumos.cds.client.ICommonDataServiceRestClient;
43 import org.acumos.cds.domain.MLPArtifact;
44 import org.acumos.cds.domain.MLPDocument;
45 import org.acumos.cds.domain.MLPSolution;
46 import org.acumos.cds.domain.MLPSolutionRevision;
47 import org.acumos.cds.domain.MLPTag;
48 import org.acumos.cds.transport.RestPageRequest;
49 import org.acumos.cds.transport.RestPageResponse;
50 import org.acumos.federation.gateway.cds.AccessType;
51 import org.acumos.federation.gateway.cds.Artifact;
52 import org.acumos.federation.gateway.cds.Document;
53 import org.acumos.federation.gateway.cds.Solution;
54 import org.acumos.federation.gateway.cds.SolutionRevision;
55 import org.acumos.federation.gateway.cds.TimestampedEntity;
56 import org.acumos.federation.gateway.service.CatalogService;
57 import org.acumos.federation.gateway.service.CatalogServiceConfiguration;
58 import org.acumos.federation.gateway.service.ServiceContext;
59 import org.acumos.federation.gateway.service.ServiceException;
60 import org.acumos.federation.gateway.util.Errors;
61 import org.apache.commons.beanutils.PropertyUtils;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64 import org.springframework.beans.factory.annotation.Autowired;
65 import org.springframework.stereotype.Service;
66 import org.springframework.web.client.HttpStatusCodeException;
67
68 /**
69  * CDS based implementation of the CatalogService.
70  *
71  */
72 @Service
73 public class CatalogServiceImpl extends AbstractServiceImpl
74                                                                                                                                 implements CatalogService {
75
76         private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
77
78         private static final List<String> allATs = new ArrayList<String>();
79
80         static {
81                 for (AccessType atc: AccessType.values()) {
82                         allATs.add(atc.code());
83                 }
84         }
85
86         @Autowired
87         private CatalogServiceConfiguration config;
88
89         @PostConstruct
90         public void initService() {
91         }
92
93         @Override
94         public List<MLPSolution> getSolutions(Map<String, ?> theSelector, ServiceContext theContext) throws ServiceException {
95                 log.debug("getSolutions with selector {}", theSelector);
96
97                 Map<String, Object> selector = new HashMap<String, Object>(this.config.getSolutionsSelectorDefaults());
98                 if (theSelector != null)
99                         selector.putAll(theSelector);
100                 //it is essential that this gets done at the end as to force all baseSelector criteria (otherwise a submitted accessTypeCode
101                 //could overwrite the basic one end expose non public solutions ..).
102                 selector.putAll(this.config.getSolutionsSelector());
103                 log.debug("getSolutions with full selector {}", selector);
104
105                 RestPageRequest pageRequest = new RestPageRequest(0, this.cdsConfig.getPageSize());
106                 RestPageResponse<MLPSolution> pageResponse = null;
107                 List<MLPSolution> solutions = new ArrayList<MLPSolution>(),
108                                                                                                 pageSolutions = null;
109                 ICommonDataServiceRestClient cdsClient = getClient(theContext);
110                 try {
111                         Predicate<MLPSolution> matcher = ServiceImpl.compileSelector(selector);
112                         String catid = (String)selector.get(Solution.Fields.catalogId);
113                         Function<RestPageRequest, RestPageResponse<MLPSolution>> pager = null;
114                         if (catid != null) {
115                                 pager = page -> cdsClient.getSolutionsInCatalog(catid, page);
116                         } else {
117                                 boolean active = (Boolean)selector.getOrDefault(Solution.Fields.active, Boolean.TRUE);
118                                 Object o = selector.getOrDefault(Solution.Fields.accessTypeCode, allATs);
119                                 String[] codes = null;
120                                 if (o instanceof String) {
121                                         codes = new String[] { (String)o };
122                                 } else {
123                                         codes = ((List<String>)o).toArray(new String[0]);
124                                 }
125                                 String[] xcodes = codes;
126                                 Instant since = Instant.ofEpochSecond((Long)selector.get(Solution.Fields.modified));
127                                 pager = page -> cdsClient.findSolutionsByDate(active, xcodes, since, page);
128                         }
129                         do {
130                                 log.debug("getSolutions page {}", pageResponse);
131                                 pageResponse = pager.apply(pageRequest);
132                         
133                                 log.debug("getSolutions page response {}", pageResponse);
134                                 //we need to post-process all other selection criteria
135                                 pageSolutions = pageResponse.getContent().stream()
136                                                                                                                         .filter(matcher)
137                                                                                                                         .collect(Collectors.toList());
138                                 log.debug("getSolutions page selection {}", pageSolutions);
139                 
140                                 pageRequest.setPage(pageResponse.getNumber() + 1);
141                                 solutions.addAll(pageSolutions);
142                         } while (!pageResponse.isLast());
143                 }
144                 catch (HttpStatusCodeException restx) {
145                         if (Errors.isCDSNotFound(restx))
146                                 return Collections.EMPTY_LIST;
147                         else {
148                                 log.debug("getSolutions failed {}: {}", restx, restx.getResponseBodyAsString());
149                                 throw new ServiceException("Failed to retrieve solutions", restx);
150                         }
151                 }
152
153                 log.debug("getSolutions: solutions count {}", solutions.size());
154                 return solutions;
155         }
156
157         @Override
158         public Solution getSolution(String theSolutionId, ServiceContext theContext) throws ServiceException {
159
160                 log.trace("getSolution {}", theSolutionId);
161                 ICommonDataServiceRestClient cdsClient = getClient(theContext, true);
162                 try {
163                         Solution solution = (Solution)cdsClient.getSolution(theSolutionId);
164                         List<MLPSolutionRevision> revisions = getSolutionRevisions(theSolutionId, theContext);
165
166                         //we can expose this solution only if we can expose at least one revision
167                         if (revisions == null || revisions.isEmpty())
168                                 return null;
169
170                         solution.setRevisions(revisions);
171                         return solution;
172                 }
173                 catch (HttpStatusCodeException restx) {
174                         if (Errors.isCDSNotFound(restx))
175                                 return null;
176                         else
177                                 throw new ServiceException("Failed to retrieve solution information", restx);
178                 }
179         }
180
181         @Override       
182         public Solution putSolution(Solution theSolution, ServiceContext theContext) throws ServiceException {
183                 
184                 log.trace("putSolution {}", theSolution);
185                 ICommonDataServiceRestClient cdsClient = getClient(theContext, true);
186
187                 //we handle tags separately
188                 Set<MLPTag> tags = theSolution.getTags();
189                 theSolution.setTags(Collections.EMPTY_SET);
190
191                 try {
192                         if (theSolution.getCreated() == TimestampedEntity.ORIGIN) {
193                                 theSolution = (Solution)cdsClient.createSolution(theSolution);  
194                         }
195                         else {
196                                 cdsClient.updateSolution(theSolution);
197                         }
198                 }
199                 catch (HttpStatusCodeException scx) {
200                         log.error("CDS solution call failed. CDS says " + scx.getResponseBodyAsString(), scx);
201                         throw new ServiceException("CDS solution call failed. CDS says " + scx.getResponseBodyAsString(), scx);
202                 }
203                 catch (Exception x) {
204                         log.error("Solution handling unexpected failure", x);
205                         throw new ServiceException("Solution handling unexpected failure", x);
206                 }
207
208                 //tags: best effort approach
209                 for (MLPTag tag: tags) {
210                         try {
211                                 cdsClient.addSolutionTag(theSolution.getSolutionId(), tag.getTag());
212                         }
213                         catch (HttpStatusCodeException scx) {
214                                 //we ignore and keep trying
215                                 log.error("CDS solution add tag call failed. CDS says " + scx.getResponseBodyAsString(), scx);
216                         }
217                 }
218
219                 //set back the tags; should we only set back those that succeded ?
220                 theSolution.setTags(tags);
221
222                 return theSolution;
223         }
224
225         @Override
226         public List<MLPSolutionRevision> getSolutionRevisions(String theSolutionId, ServiceContext theContext) throws ServiceException {
227
228                 log.trace("getSolutionRevisions {}", theSolutionId);
229                 try {
230                         List<MLPSolutionRevision> revisions = getClient(theContext).getSolutionRevisions(theSolutionId);
231                         //make sure we only expose revisions according to the filter
232                         if (revisions != null) {
233                                 log.trace("getSolutionRevisions {}: got {} revisions", theSolutionId, revisions.size());
234                                 revisions = 
235                                         revisions.stream()
236                                                                          .filter(revision -> this.config.getSolutionRevisionsSelector().entrySet().stream()
237                                                                                                                                                                 .allMatch(s -> {
238                                                                                                                                                                         try {
239                                                                                                                                                                                 log.trace("getSolutionRevisions verifying filter: revision property value {} vs filter value {}", PropertyUtils.getProperty(revision, s.getKey()), s.getValue());
240                                                                                                                                                                                 return PropertyUtils.getProperty(revision, s.getKey()).equals(s.getValue());
241                                                                                                                                                                         } 
242                                                                                                                                                                         catch (Exception x) { 
243                                                                                                                                                                                 log.trace("getSolutionRevisions failed to verify filter", x);
244                                                                                                                                                                                 return false;
245                                                                                                                                                                         }
246                                                                                                                                                                 })
247                                                                                                         )
248                                                                          .collect(Collectors.toList());
249                         }
250                         return revisions;
251                 }
252                 catch (HttpStatusCodeException restx) {
253                         if (Errors.isCDSNotFound(restx))
254                                 return null;
255                         else
256                                 throw new ServiceException("Failed to retrieve solution revision information", restx);
257                 }
258         }
259
260
261         @Override
262         public SolutionRevision getSolutionRevision(String theSolutionId, String theRevisionId,
263                         ServiceContext theContext) throws ServiceException {
264
265                 log.trace("getSolutionRevision");
266                 ICommonDataServiceRestClient cdsClient = getClient(theContext, true);
267                 try {
268                         SolutionRevision revision =
269                                         (SolutionRevision)cdsClient.getSolutionRevision(theSolutionId, theRevisionId);
270                         revision.setArtifacts(getSolutionRevisionArtifacts(theSolutionId, theRevisionId, theContext));
271                         revision.setDocuments(getSolutionRevisionDocuments(theSolutionId, theRevisionId, theContext));
272                         try {
273                                 revision.setRevisionDescription(cdsClient.getRevisionDescription(theRevisionId, AccessTypeCode.PB.name()));
274                         }
275                         catch (HttpStatusCodeException restx) {
276                                 if (!Errors.isCDSNotFound(restx))
277                                         throw new ServiceException("Failed to retrieve solution revision description", restx);
278                         }
279         
280                         return revision;
281                 }       
282                 catch (HttpStatusCodeException restx) {
283                         if (Errors.isCDSNotFound(restx))
284                                 return null;
285                         else
286                                 throw new ServiceException("Failed to retrieve solution revision information", restx);
287                 }
288         }
289
290         @Override
291   public SolutionRevision putSolutionRevision(SolutionRevision theRevision, ServiceContext theContext)
292                                                                                                                                                                                                                                                                                                                                                                                                                                 throws ServiceException {
293                 log.trace("putSolutionRevision {}", theRevision);
294         
295                 try {
296                         if (theRevision.getCreated() == TimestampedEntity.ORIGIN) {
297                                 theRevision = (SolutionRevision)getClient(theContext).createSolutionRevision(theRevision);
298         }
299                         else {
300                                 getClient(theContext).updateSolutionRevision(theRevision);
301                         }
302                 }
303                 catch (HttpStatusCodeException scx) {
304                         log.error("CDS solution revision call failed. CDS says " + scx.getResponseBodyAsString(), scx);
305                         throw new ServiceException("CDS solution revision call failed. CDS says " + scx.getResponseBodyAsString(), scx);
306                 }
307                 catch (Exception x) {
308                         log.error("Solution revision handling unexpected failure", x);
309                         throw new ServiceException("Solution revision handling unexpected failure", x);
310                 }
311
312                 return theRevision;
313         }
314
315         @Override
316         public List<MLPArtifact> getSolutionRevisionArtifacts(String theSolutionId, String theRevisionId,
317                         ServiceContext theContext) throws ServiceException {
318
319                 log.trace("getSolutionRevisionArtifacts");
320                 try {
321                         return getClient(theContext).getSolutionRevisionArtifacts(theSolutionId, theRevisionId);
322                 }
323                 catch (HttpStatusCodeException restx) {
324                         if (Errors.isCDSNotFound(restx))
325                                 return null;
326                         else
327                                 throw new ServiceException("Failed to retrieve solution revision artifacts information", restx);
328                 }
329         }
330
331         /**
332          * @return catalog artifact representation
333          * @throws ServiceException if failing to retrieve artifact information or retrieve content 
334          */
335         @Override
336         public Artifact getSolutionRevisionArtifact(String theArtifactId, ServiceContext theContext) throws ServiceException {
337
338                 log.trace("getSolutionRevisionArtifact");
339                 try {
340                         //one should check that this belongs to at least one public revision of some solution accessible within the given context ..
341                         return (Artifact)getClient(theContext).getArtifact(theArtifactId);
342                 }       
343                 catch (HttpStatusCodeException restx) {
344                         if (Errors.isCDSNotFound(restx))
345                                 return null;
346                         else
347                                 throw new ServiceException("Failed to retrieve solution revision artifact information", restx);
348                 }
349         }
350
351         @Override
352         public List<MLPDocument> getSolutionRevisionDocuments(String theSolutionId, String theRevisionId, ServiceContext theContext) throws ServiceException {
353                 log.trace("getSolutionRevisionDocuments");
354                 try {
355                         return getClient(theContext).getSolutionRevisionDocuments(theRevisionId, AccessTypeCode.PB.name());
356                 }
357                 catch (HttpStatusCodeException restx) {
358                         if (Errors.isCDSNotFound(restx))
359                                 return null;
360                         else
361                                 throw new ServiceException("Failed to retrieve solution revision documents information", restx);
362                 }
363         }
364
365         @Override
366         public Document getSolutionRevisionDocument(String theDocumentId, ServiceContext theContext) throws ServiceException {
367                 log.trace("getSolutionRevisionDocument");
368                 try {
369                         //one should check that this has a public visibility within at least one revision of some solution accessible within the given context ..
370                         return (Document)getClient(theContext).getDocument(theDocumentId);
371                 }       
372                 catch (HttpStatusCodeException restx) {
373                         if (Errors.isCDSNotFound(restx))
374                                 return null;
375                         else
376                                 throw new ServiceException("Failed to retrieve solution revision document information", restx);
377                 }
378         }
379
380 }