Added the new versioning validation for policy and policy type
[policy/api.git] / main / src / main / java / org / onap / policy / api / main / rest / provider / CommonModelProvider.java
1 /*-\r
2  * ============LICENSE_START=======================================================\r
3  * ONAP Policy API\r
4  * ================================================================================\r
5  * Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.\r
6  * Modifications Copyright (C) 2019 Nordix Foundation.\r
7  * ================================================================================\r
8  * Licensed under the Apache License, Version 2.0 (the "License");\r
9  * you may not use this file except in compliance with the License.\r
10  * You may obtain a copy of the License at\r
11  *\r
12  *      http://www.apache.org/licenses/LICENSE-2.0\r
13  *\r
14  * Unless required by applicable law or agreed to in writing, software\r
15  * distributed under the License is distributed on an "AS IS" BASIS,\r
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
17  * See the License for the specific language governing permissions and\r
18  * limitations under the License.\r
19  *\r
20  * SPDX-License-Identifier: Apache-2.0\r
21  * ============LICENSE_END=========================================================\r
22  */\r
23 \r
24 package org.onap.policy.api.main.rest.provider;\r
25 \r
26 import java.util.ArrayList;\r
27 import java.util.HashMap;\r
28 import java.util.List;\r
29 import java.util.Map;\r
30 import java.util.function.BiConsumer;\r
31 import javax.ws.rs.core.Response;\r
32 \r
33 import org.apache.commons.lang3.tuple.Pair;\r
34 import org.onap.policy.api.main.parameters.ApiParameterGroup;\r
35 import org.onap.policy.common.parameters.ParameterService;\r
36 import org.onap.policy.models.base.PfConceptKey;\r
37 import org.onap.policy.models.base.PfModelException;\r
38 import org.onap.policy.models.pdp.concepts.PdpGroup;\r
39 import org.onap.policy.models.pdp.concepts.PdpGroupFilter;\r
40 import org.onap.policy.models.pdp.concepts.PdpSubGroup;\r
41 import org.onap.policy.models.pdp.enums.PdpState;\r
42 import org.onap.policy.models.provider.PolicyModelsProvider;\r
43 import org.onap.policy.models.provider.PolicyModelsProviderFactory;\r
44 import org.onap.policy.models.provider.PolicyModelsProviderParameters;\r
45 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;\r
46 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;\r
47 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;\r
48 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;\r
49 \r
50 /**\r
51  * Super class for providers that use a model provider.\r
52  */\r
53 public class CommonModelProvider implements AutoCloseable {\r
54 \r
55     protected final PolicyModelsProvider modelsProvider;\r
56 \r
57     /**\r
58      * Constructs the object, populating {@link #modelsProvider}.\r
59      *\r
60      * @throws PfModelException if an error occurs\r
61      */\r
62     public CommonModelProvider() throws PfModelException {\r
63 \r
64         ApiParameterGroup parameterGroup = ParameterService.get("ApiGroup");\r
65         PolicyModelsProviderParameters providerParameters = parameterGroup.getDatabaseProviderParameters();\r
66         modelsProvider = new PolicyModelsProviderFactory().createPolicyModelsProvider(providerParameters);\r
67     }\r
68 \r
69     /**\r
70      * Closes the connection to database.\r
71      *\r
72      * @throws PfModelException the PfModel parsing exception\r
73      */\r
74     @Override\r
75     public void close() throws PfModelException {\r
76 \r
77         modelsProvider.close();\r
78     }\r
79 \r
80     /**\r
81      * Checks if service template contains any policy.\r
82      *\r
83      * @param serviceTemplate the service template to check against\r
84      *\r
85      * @return boolean whether service template contains any policy\r
86      */\r
87     protected boolean hasPolicy(ToscaServiceTemplate serviceTemplate) {\r
88 \r
89         return hasData(serviceTemplate.getToscaTopologyTemplate().getPolicies());\r
90     }\r
91 \r
92     /**\r
93      * Checks if service template contains any policy type.\r
94      *\r
95      * @param serviceTemplate the service template to check against\r
96      *\r
97      * @return boolean whether service template contains any policy type\r
98      */\r
99     protected boolean hasPolicyType(ToscaServiceTemplate serviceTemplate) {\r
100 \r
101         return hasData(serviceTemplate.getPolicyTypes());\r
102     }\r
103 \r
104     /**\r
105      * Checks if the first element of a list of maps contains data.\r
106      *\r
107      * @param listOfMapsToCheck list of maps to be examined\r
108      * @return {@code true} if the list contains data, {@code false} otherwise\r
109      */\r
110     protected <T> boolean hasData(List<Map<String, T>> listOfMapsToCheck) {\r
111 \r
112         return (listOfMapsToCheck != null && !listOfMapsToCheck.isEmpty() && !listOfMapsToCheck.get(0).isEmpty());\r
113     }\r
114 \r
115 \r
116     /**\r
117      * Checks if a maps contains data.\r
118      *\r
119      * @param mapToCheck map to be examined\r
120      * @return {@code true} if the list contains data, {@code false} otherwise\r
121      */\r
122     protected <T> boolean hasData(Map<String, T> mapToCheck) {\r
123 \r
124         // We don't allow a null or empty map as well as a map entry with a valid key but null value\r
125         return (mapToCheck != null && !mapToCheck.isEmpty() && !mapToCheck.containsValue(null));\r
126     }\r
127 \r
128     /**\r
129      * Validates that some text represents a number.\r
130      *\r
131      * @param text text to be validated\r
132      * @param errorMsg error message included in the exception, if the text is not a valid\r
133      *        number\r
134      * @throws PfModelException if the text is not a valid number\r
135      */\r
136     protected void validNumber(String text, String errorMsg) throws PfModelException {\r
137         try {\r
138             Integer.parseInt(text);\r
139 \r
140         } catch (NumberFormatException exc) {\r
141             throw new PfModelException(Response.Status.BAD_REQUEST, errorMsg, exc);\r
142         }\r
143     }\r
144 \r
145     /**\r
146      * Constructs returned message for policy delete rule violation.\r
147      *\r
148      * @param policyId the ID of policy\r
149      * @param policyVersion the version of policy\r
150      * @param pdpGroups the list of pdp groups\r
151      *\r
152      * @return the constructed message\r
153      */\r
154     protected String constructDeletePolicyViolationMessage(String policyId, String policyVersion,\r
155                     List<PdpGroup> pdpGroups) {\r
156 \r
157         List<String> pdpGroupNameVersionList = new ArrayList<>(pdpGroups.size());\r
158         for (PdpGroup pdpGroup : pdpGroups) {\r
159             pdpGroupNameVersionList.add(pdpGroup.getName() + ":" + pdpGroup.getVersion());\r
160         }\r
161         String deployedPdpGroups = String.join(",", pdpGroupNameVersionList);\r
162         return "policy with ID " + policyId + ":" + policyVersion\r
163                         + " cannot be deleted as it is deployed in pdp groups " + deployedPdpGroups;\r
164     }\r
165 \r
166     /**\r
167      * Constructs returned message for policy type delete rule violation.\r
168      *\r
169      * @param policyTypeId the ID of policy type\r
170      * @param policyTypeVersion the version of policy type\r
171      * @param policies the list of policies that parameterizes specified policy type\r
172      *\r
173      * @return the constructed message\r
174      */\r
175     protected String constructDeletePolicyTypeViolationMessage(String policyTypeId, String policyTypeVersion,\r
176                     List<ToscaPolicy> policies) {\r
177 \r
178         List<String> policyNameVersionList = new ArrayList<>(policies.size());\r
179         for (ToscaPolicy policy : policies) {\r
180             policyNameVersionList.add(policy.getName() + ":" + policy.getVersion());\r
181         }\r
182         String parameterizedPolicies = String.join(",", policyNameVersionList);\r
183         return "policy type with ID " + policyTypeId + ":" + policyTypeVersion\r
184                         + " cannot be deleted as it is parameterized by policies " + parameterizedPolicies;\r
185     }\r
186 \r
187     /**\r
188      * Collects all deployed versions of specified policy in all pdp groups.\r
189      *\r
190      * @param policyId the ID of policy\r
191      * @param policyType the concept key of policy type\r
192      * @param getter the custom generic getter Bifunction\r
193      * @param consumer the BiConsumer\r
194      * @param data the data structure storing retrieved deployed policies\r
195      *\r
196      * @return a map between pdp group and deployed versions of specified policy in that group\r
197      *\r
198      * @throws PfModelException the PfModel parsing exception\r
199      */\r
200     protected <T, R> Map<Pair<String, String>, T> collectDeployedPolicies(String policyId, PfConceptKey policyType,\r
201             BiFunctionWithEx<String, String, R> getter, BiConsumer<T, R> consumer, T data) throws PfModelException {\r
202 \r
203         List<PdpGroup> pdpGroups = getPolicyTypeFilteredPdpGroups(policyType);\r
204         hasActivePdpGroup(pdpGroups, policyType, policyId);\r
205         return constructDeployedPolicyMap(pdpGroups, policyId, policyType, getter, consumer, data);\r
206     }\r
207 \r
208     @FunctionalInterface\r
209     protected interface BiFunctionWithEx<T,U,R> {\r
210         public R apply(T value1, U value2) throws PfModelException;\r
211     }\r
212 \r
213     /**\r
214      * Checks if the list of pdp groups is empty.\r
215      * If so, throws exception saying specified policy deployment is not found in all existing pdp groups.\r
216      *\r
217      * @param pdpGroups the list of pdp groups to check against\r
218      * @param policyType the concept key of policy type\r
219      * @param policyId the ID of policy\r
220      *\r
221      * @throws PfModelException the PfModel parsing exception\r
222      */\r
223     private void hasActivePdpGroup(List<PdpGroup> pdpGroups, PfConceptKey policyType, String policyId)\r
224             throws PfModelException {\r
225 \r
226         if (pdpGroups.isEmpty()) {\r
227             throw new PfModelException(Response.Status.NOT_FOUND,\r
228                     constructDeploymentNotFoundMessage(policyType, policyId));\r
229         }\r
230     }\r
231 \r
232     /**\r
233      * Retrieves all pdp groups supporting specified policy type.\r
234      *\r
235      * @param policyTypeId the ID of policy type\r
236      * @param policyTypeVersion the version of policy type\r
237      *\r
238      * @return a list of pdp groups supporting specified policy type\r
239      *\r
240      * @throws PfModelException the PfModel parsing exception\r
241      */\r
242     private List<PdpGroup> getPolicyTypeFilteredPdpGroups(PfConceptKey policyType)\r
243             throws PfModelException {\r
244 \r
245         List<ToscaPolicyTypeIdentifier> policyTypes = new ArrayList<>();\r
246         policyTypes.add(new ToscaPolicyTypeIdentifier(policyType.getName(), policyType.getVersion()));\r
247         PdpGroupFilter pdpGroupFilter = PdpGroupFilter.builder().policyTypeList(policyTypes)\r
248                 .groupState(PdpState.ACTIVE).pdpState(PdpState.ACTIVE).build();\r
249         return modelsProvider.getFilteredPdpGroups(pdpGroupFilter);\r
250     }\r
251 \r
252     /**\r
253      * Constructs the map of deployed pdp groups and deployed policies.\r
254      *\r
255      * @param pdpGroups the list of pdp groups that contain the specified policy\r
256      * @param policyId the ID of policy\r
257      * @param policyType the concept key of policy type\r
258      * @param getter the custom generic getter BiFunction\r
259      * @param consumer the BiConsumer\r
260      * @param data the data structure storing retrieved deployed policies\r
261      *\r
262      * @return the constructed map of pdp groups and deployed policies\r
263      *\r
264      * @throws PfModelException the PfModel parsing exception\r
265      */\r
266     private <T, R> Map<Pair<String, String>, T> constructDeployedPolicyMap(List<PdpGroup> pdpGroups, String policyId,\r
267             PfConceptKey policyType, BiFunctionWithEx<String, String, R> getter, BiConsumer<T, R> consumer, T data)\r
268                     throws PfModelException {\r
269 \r
270         Map<Pair<String, String>, T> deployedPolicyMap = new HashMap<>();\r
271         for (PdpGroup pdpGroup : pdpGroups) {\r
272             List<ToscaPolicyIdentifier> policyIdentifiers = extractPolicyIdentifiers(policyId, pdpGroup, policyType);\r
273             T deployedPolicies = getDeployedPolicies(policyIdentifiers, policyType, getter, consumer, data);\r
274             deployedPolicyMap.put(Pair.of(pdpGroup.getName(), pdpGroup.getVersion()), deployedPolicies);\r
275         }\r
276         return deployedPolicyMap;\r
277     }\r
278 \r
279     /**\r
280      * Extracts policy identifiers matching specified policy ID from specified pdp group.\r
281      *\r
282      * @param policyId the ID of policy to match\r
283      * @param pdpGroup the target pdp group to search\r
284      * @param policyType the concept key of policy type\r
285      *\r
286      * @return the list of policy identifiers\r
287      *\r
288      * @throws PfModelException the PfModel parsing exception\r
289      */\r
290     private List<ToscaPolicyIdentifier> extractPolicyIdentifiers(String policyId, PdpGroup pdpGroup,\r
291             PfConceptKey policyType) throws PfModelException {\r
292 \r
293         List<ToscaPolicyIdentifier> policyIdentifiers = new ArrayList<>();\r
294         for (PdpSubGroup pdpSubGroup : pdpGroup.getPdpSubgroups()) {\r
295             for (ToscaPolicyIdentifier policyIdentifier : pdpSubGroup.getPolicies()) {\r
296                 if (policyId.equalsIgnoreCase(policyIdentifier.getName())) {\r
297                     policyIdentifiers.add(policyIdentifier);\r
298                 }\r
299             }\r
300         }\r
301         if (policyIdentifiers.isEmpty()) {\r
302             throw new PfModelException(Response.Status.NOT_FOUND,\r
303                     constructDeploymentNotFoundMessage(policyType, policyId));\r
304         }\r
305         return policyIdentifiers;\r
306     }\r
307 \r
308     /**\r
309      * Retrieves deployed policies in a generic way.\r
310      *\r
311      * @param policyIdentifiers the identifiers of the policies to return\r
312      * @param policyType the concept key of current policy type\r
313      * @param getter the method reference of getting deployed policies\r
314      * @param consumer the method reference of consuming the returned policies\r
315      * @param data the data structure of deployed policies to return\r
316      *\r
317      * @return the generic type of policy data structure to return\r
318      *\r
319      * @throws PfModelException the PfModel parsing exception\r
320      */\r
321     private <T, R> T getDeployedPolicies(List<ToscaPolicyIdentifier> policyIdentifiers, PfConceptKey policyType,\r
322             BiFunctionWithEx<String, String, R> getter, BiConsumer<T, R> consumer, T data) throws PfModelException {\r
323 \r
324         for (ToscaPolicyIdentifier policyIdentifier : policyIdentifiers) {\r
325             R result = getter.apply(policyIdentifier.getName(),\r
326                     getTrimedVersionForLegacyType(policyIdentifier.getVersion(), policyType));\r
327             consumer.accept(data, result);\r
328         }\r
329         return data;\r
330     }\r
331 \r
332     /**\r
333      * Trims the version for legacy policies.\r
334      *\r
335      * @param fullVersion the full version format with major, minor, patch\r
336      * @param policyType the concept key of policy type\r
337      *\r
338      * @return the trimmed version\r
339      */\r
340     private String getTrimedVersionForLegacyType(String fullVersion, PfConceptKey policyType) {\r
341         return (policyType.getName().contains("guard")\r
342                 || policyType.getName().contains("Operational")) ? fullVersion.split("\\.")[0] : fullVersion;\r
343     }\r
344 \r
345     /**\r
346      * Constructs returned message for not found policy deployment.\r
347      *\r
348      * @param policyType the concept key of policy type\r
349      * @param policyId the ID of policy\r
350      *\r
351      * @return constructed message\r
352      */\r
353     private String constructDeploymentNotFoundMessage(PfConceptKey policyType, String policyId) {\r
354 \r
355         return "could not find policy with ID " + policyId + " and type "\r
356                 + policyType.getName() + ":" + policyType.getVersion() + " deployed in any pdp group";\r
357     }\r
358 }\r