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