Add catalogId field to catalog search selector
[federation.git] / gateway / src / main / java / org / acumos / federation / gateway / service / impl / ServiceImpl.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 package org.acumos.federation.gateway.service.impl;
22
23 import java.lang.invoke.MethodHandles;
24 import java.time.Instant;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.function.Function;
30 import java.util.function.Predicate;
31
32 import org.acumos.cds.domain.MLPSolution;
33 import org.acumos.cds.domain.MLPTag;
34 import org.acumos.federation.gateway.cds.Solution;
35 import org.acumos.federation.gateway.service.ServiceException;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * Some basic tooling for service implementation.
41  * Common functionality to be re-used across service implementations.
42  */
43 public abstract class ServiceImpl {
44
45         private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
46
47         private ServiceImpl() {
48         }
49
50
51         /**
52          * Returns a predicate equivalent to the logical AND of any non-null argument predicates.
53          * @param preds predicates to combine
54          * @return a predicate computing the logical AND of any non-null arguments or a predicate returning true, if there are none
55          */
56         private static Predicate<MLPSolution> and(Predicate<MLPSolution> ... preds) {
57                 Predicate<MLPSolution> ret = null;
58                 for (Predicate<MLPSolution> x: preds) {
59                         if (ret == null) {
60                                 ret = x;
61                         } else if (x != null) {
62                                 ret = ret.and(x);
63                         }
64                 }
65                 if (ret == null) {
66                         ret = (arg) -> true;
67                 }
68                 return(ret);
69         }
70
71
72         /**
73          * Returns a predicate determining matching against a multi-valued field.
74          * The returned predicate will be called with an MLPSolution.  The
75          * Function specified by field will be invoked on it, to extract a Set
76          * of Strings, which will be tested against the value, in theSelector,
77          * corresponding to key.
78          * If theSelector does not contain key, this just returns null.
79          * Otherwise, if the value is a String, this returns a predicate
80          * computing whether the value is in the extracted Set of Strings.
81          * Otherwise, if the value is a List, this returns a predicate
82          * computing whether any of the values in the List is contained in
83          * the Set of Strings.
84          * @param theSelector a map of field names to expected values
85          * @param key the field name to be handled by this predicate
86          * @param field the Function to extract the field value from the Solution
87          * @param listok whether this field supports a list of values in the selector
88          * @return the predicate for testing the field value
89          * @throws ServiceException if the value of key, in theSelector is neither a String nor a List.
90          */
91
92         private static Predicate<MLPSolution> contains(Map<String, ?> theSelector, String key, Function<MLPSolution, Set<String>> field, boolean listok) throws ServiceException {
93                 Object o = theSelector.get(key);
94                 if (o == null) {
95                         return(null);
96                 }
97                 log.trace("using {} based selection {}", key, o);
98                 if (o instanceof String) {
99                         String s = (String)o;
100                         return(arg-> field.apply(arg).contains(s));
101                 }
102                 if (listok && o instanceof List) {
103                         List l = (List)o;
104                         return(arg -> {
105                                 for (Object val: field.apply(arg)) {
106                                         if (l.contains(val)) {
107                                                 return(true);
108                                         }
109                                 }
110                                 return(false);
111                         });
112                 }
113                 log.debug("unknown {} criteria representation {}", key, o.getClass().getName());
114                 throw new ServiceException("Invalid Selector");
115         }
116
117
118         /**
119          * Returns a predicate determining matching against a field.
120          * The returned predicate will be called with an MLPSolution.  The
121          * Function specified by field will be invoked on it, to extract its
122          * value, which will be tested against the value, in theSelector,
123          * corresponding to key.
124          * If theSelector does not contain key, this just returns null.
125          * Otherwise, if theSelector contains a String, this returns a predicate
126          * computing whether the value equals the extracted value.
127          * Otherwise, if the value is a List, this returns a predicate
128          * computing whether the extracted value is contained in the List.
129          * @param theSelector a map of field names to expected values
130          * @param key the field name to be handled by this predicate
131          * @param field the Function to extract the field value from the Solution
132          * @param listok whether this field supports a list of values in the selector
133          * @return the predicate for testing the field value
134          * @throws ServiceException if the value of key, in theSelector is neither a String nor a List.
135          */
136
137         private static Predicate<MLPSolution> has(Map<String, ?> theSelector, String key, Function<MLPSolution, String> field, boolean listok) throws ServiceException {
138                 Object o = theSelector.get(key);
139                 if (o == null) {
140                         return(null);
141                 }
142                 log.trace("using {} based selection {}", key, o);
143                 if (o instanceof String) {
144                         String s = (String)o;
145                         return(arg -> s.equals(field.apply(arg)));
146                 }
147                 if (listok && o instanceof List) {
148                         List l = (List)o;
149                         return(arg -> l.contains(field.apply(arg)));
150                 }
151                 log.debug("unknown {} criteria representation {}", key, o.getClass().getName());
152                 throw new ServiceException("Invalid Selector");
153         }
154
155
156         /**
157          * Returns a predicate for testing an MLPSolution against a selector.
158          * @param theSelector the criteria to be met in a matching solution
159          * @return a predicate for checking for matching solutions
160          * @throws ServiceException if theSelector is malformed
161          */
162
163         public static Predicate<MLPSolution> compileSelector(Map<String, ?> theSelector) throws ServiceException {
164                 if (theSelector == null) {
165                         return(arg -> true);
166                 }
167                 log.trace("compileSelector {}", theSelector);
168                 Boolean ao = (Boolean)theSelector.get(Solution.Fields.active);
169                 boolean active = ao == null? true: ao.booleanValue();
170                 Instant since = Instant.ofEpochSecond((Long)theSelector.get(Solution.Fields.modified));
171                 return(and(
172                         arg -> arg.isActive() == active,
173                         arg -> arg.getModified().compareTo(since) >= 0,
174                         has(theSelector, Solution.Fields.solutionId, arg -> arg.getSolutionId(), false),
175                         has(theSelector, Solution.Fields.modelTypeCode, arg -> arg.getModelTypeCode(), true),
176                         has(theSelector, Solution.Fields.toolkitTypeCode, arg -> arg.getToolkitTypeCode(), true),
177                         contains(theSelector, Solution.Fields.tags, arg -> {
178                                 Set<String> ret = new HashSet<String>();
179                                 for (MLPTag tag: arg.getTags()) {
180                                         ret.add(tag.getTag());
181                                 }
182                                 return(ret);
183                         }, true),
184                         has(theSelector, Solution.Fields.name, arg ->arg.getName(), false)
185                 ));
186         }
187 }