Spring repository and service layer for policy-api
[policy/api.git] / main / src / main / java / org / onap / policy / api / main / service / ToscaServiceTemplateService.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2022 Bell Canada. All rights reserved.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.api.main.service;
22
23 import java.util.List;
24 import javax.ws.rs.core.Response;
25 import lombok.NonNull;
26 import lombok.RequiredArgsConstructor;
27 import org.apache.commons.collections4.CollectionUtils;
28 import org.onap.policy.api.main.repository.ToscaServiceTemplateRepository;
29 import org.onap.policy.api.main.rest.PolicyFetchMode;
30 import org.onap.policy.common.parameters.BeanValidationResult;
31 import org.onap.policy.models.base.PfConceptKey;
32 import org.onap.policy.models.base.PfModelException;
33 import org.onap.policy.models.base.PfModelRuntimeException;
34 import org.onap.policy.models.tosca.authorative.concepts.ToscaEntityFilter;
35 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
36 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType;
37 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
38 import org.onap.policy.models.tosca.authorative.concepts.ToscaTypedEntityFilter;
39 import org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicies;
40 import org.onap.policy.models.tosca.simple.concepts.JpaToscaPolicyTypes;
41 import org.onap.policy.models.tosca.simple.concepts.JpaToscaServiceTemplate;
42 import org.onap.policy.models.tosca.simple.concepts.JpaToscaTopologyTemplate;
43 import org.onap.policy.models.tosca.simple.provider.SimpleToscaProvider;
44 import org.onap.policy.models.tosca.utils.ToscaServiceTemplateUtils;
45 import org.onap.policy.models.tosca.utils.ToscaUtils;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.springframework.stereotype.Service;
49 import org.springframework.transaction.annotation.Transactional;
50
51 @Service
52 @Transactional
53 @RequiredArgsConstructor
54 public class ToscaServiceTemplateService {
55
56     private static final Logger LOGGER = LoggerFactory.getLogger(ToscaServiceTemplateService.class);
57
58     // Recurring string constants
59     private static final String POLICY_TYPE = "policy type ";
60     private static final String NOT_FOUND = " not found";
61     public static final String SERVICE_TEMPLATE_NOT_FOUND_MSG = "service template not found in database";
62     public static final String DO_NOT_EXIST_MSG = " do not exist";
63
64     private final ToscaServiceTemplateRepository toscaServiceTemplateRepository;
65     private final PdpGroupService pdpGroupService;
66     private final PolicyTypeService policyTypeService;
67     private final PolicyService policyService;
68
69     /**
70      * Retrieves a list of policy types matching specified policy type name and version.
71      *
72      * @param policyTypeName the name of policy type
73      * @param policyTypeVersion the version of policy type
74      * @return the ToscaServiceTemplate object
75      */
76     public ToscaServiceTemplate fetchPolicyTypes(final String policyTypeName, final String policyTypeVersion)
77         throws PfModelException {
78         return getFilteredPolicyTypes(policyTypeName, policyTypeVersion);
79     }
80
81     /**
82      * Retrieves a list of policy types with the latest versions.
83      *
84      * @param policyTypeName the name of policy type
85      * @return the ToscaServiceTemplate object
86      */
87     public ToscaServiceTemplate fetchLatestPolicyTypes(final String policyTypeName) throws PfModelException {
88         return getFilteredPolicyTypes(policyTypeName, ToscaEntityFilter.LATEST_VERSION);
89     }
90
91     /**
92      * Creates a new policy type.
93      *
94      * @param body the entity body of policy type
95      * @return the TOSCA service template containing the created policy types
96      * @throws PfModelRuntimeException on errors creating policy types
97      */
98     public ToscaServiceTemplate createPolicyType(@NonNull final ToscaServiceTemplate body)
99         throws PfModelRuntimeException {
100         final var incomingServiceTemplate = new JpaToscaServiceTemplate(body);
101         LOGGER.debug("->createPolicyType: serviceTemplate={}", incomingServiceTemplate);
102
103         // assert incoming body contains policyTypes
104         ToscaUtils.assertPolicyTypesExist(incomingServiceTemplate);
105
106         // append the incoming fragment to the DB TOSCA service template
107         final var serviceTemplateToWrite =
108             ToscaServiceTemplateUtils.addFragment(getDefaultJpaToscaServiceTemplate(), incomingServiceTemplate);
109
110         final var result = serviceTemplateToWrite.validate("service template");
111         if (!result.isValid()) {
112             throw new PfModelRuntimeException(Response.Status.NOT_ACCEPTABLE, result.getResult());
113         } else {
114             toscaServiceTemplateRepository.save(serviceTemplateToWrite);
115             LOGGER.debug("<-createPolicyType: writtenServiceTemplate={}", serviceTemplateToWrite);
116         }
117         return body;
118     }
119
120     /**
121      * Delete the policy type matching specified policy type name and version.
122      *
123      * @param policyTypeName the name of policy type
124      * @param policyTypeVersion the version of policy type, if the version of the key is null,
125      *                          all versions of the policy type are deleted.
126      * @return the TOSCA service template containing the policy types that were deleted
127      * @throws PfModelRuntimeException on errors deleting policy types
128      */
129     public ToscaServiceTemplate deletePolicyType(final String policyTypeName, final String policyTypeVersion)
130         throws PfModelRuntimeException {
131         final var policyTypeKey = new PfConceptKey(policyTypeName, policyTypeVersion);
132         LOGGER.debug("->deletePolicyType: name={}, version={}", policyTypeName, policyTypeVersion);
133
134         // terminate deletion if supported in a PdpGroup
135         pdpGroupService.assertPolicyTypeNotSupportedInPdpGroup(policyTypeName, policyTypeVersion);
136
137         final var serviceTemplate = getDefaultJpaToscaServiceTemplate();
138
139         // terminate deletion if not found
140         if (!ToscaUtils.doPolicyTypesExist(serviceTemplate)) {
141             throw new PfModelRuntimeException(Response.Status.NOT_FOUND, "no policy types found");
142         }
143
144         final var policyTypeForDeletion = serviceTemplate.getPolicyTypes().get(policyTypeKey);
145         if (policyTypeForDeletion == null) {
146             throw new PfModelRuntimeException(Response.Status.NOT_FOUND,
147                 POLICY_TYPE + policyTypeKey.getId() + NOT_FOUND);
148         }
149
150         final var result = new BeanValidationResult("policy types", serviceTemplate);
151
152         for (final var policyType : serviceTemplate.getPolicyTypes().getAll(null)) {
153             final var ancestorList = ToscaUtils
154                 .getEntityTypeAncestors(serviceTemplate.getPolicyTypes(), policyType, result);
155             // terminate deletion if referenced by another via derived_from property
156             if (ancestorList.contains(policyTypeForDeletion)) {
157                 throw new PfModelRuntimeException(Response.Status.NOT_ACCEPTABLE, POLICY_TYPE + policyTypeKey.getId()
158                     + " is in use, it is referenced in policy type " + policyType.getId());
159             }
160         }
161         if (ToscaUtils.doPoliciesExist(serviceTemplate)) {
162             for (final var policy : serviceTemplate.getTopologyTemplate().getPolicies().getAll(null)) {
163                 // terminate deletion if referenced by a policy
164                 if (policyTypeKey.equals(policy.getType())) {
165                     throw new PfModelRuntimeException(Response.Status.NOT_ACCEPTABLE, POLICY_TYPE
166                         + policyTypeKey.getId() + " is in use, it is referenced in policy " + policy.getId());
167                 }
168             }
169         }
170
171         // remove policyType from service template and write to DB
172         serviceTemplate.getPolicyTypes().getConceptMap().remove(policyTypeKey);
173         toscaServiceTemplateRepository.save(serviceTemplate);
174
175         // remove the entry from the Policy table
176         policyTypeService.deletePolicyType(policyTypeKey);
177
178         // prepare return service template object
179         var deletedServiceTemplate = new JpaToscaServiceTemplate();
180         deletedServiceTemplate.setPolicyTypes(new JpaToscaPolicyTypes());
181         deletedServiceTemplate.getPolicyTypes().getConceptMap().put(policyTypeKey, policyTypeForDeletion);
182
183         LOGGER.debug("<-deletePolicyType: key={}, serviceTemplate={}", policyTypeKey, deletedServiceTemplate);
184         return deletedServiceTemplate.toAuthorative();
185     }
186
187     /**
188      * Retrieves a list of policies matching specified name and version of both policy type and policy.
189      *
190      * @param policyTypeName the name of policy type
191      * @param policyTypeVersion the version of policy type
192      * @param policyName the name of policy
193      * @param policyVersion the version of policy
194      * @param mode the fetch mode for policies
195      * @return the ToscaServiceTemplate object with the policies found
196      * @throws PfModelException on errors getting the policy
197      */
198     public ToscaServiceTemplate fetchPolicies(final String policyTypeName, final String policyTypeVersion,
199         final String policyName, final String policyVersion, final PolicyFetchMode mode) throws PfModelException {
200         return getFilteredPolicies(policyTypeName, policyTypeVersion, policyName, policyVersion, mode);
201     }
202
203     /**
204      * Retrieves a list of policies with the latest versions that match specified policy type id and version.
205      *
206      * @param policyTypeName the name of policy type
207      * @param policyTypeVersion the version of policy type
208      * @param policyName the name of the policy
209      * @param mode the fetch mode for policies
210      * @return the ToscaServiceTemplate object with the policies found
211      * @throws PfModelException on errors getting the policy
212      */
213     public ToscaServiceTemplate fetchLatestPolicies(final String policyTypeName, final String policyTypeVersion,
214         final String policyName, final PolicyFetchMode mode) throws PfModelException {
215         return getFilteredPolicies(policyTypeName, policyTypeVersion, policyName, ToscaTypedEntityFilter.LATEST_VERSION,
216             mode);
217     }
218
219     /**
220      * Creates one or more new policies for the same policy type name and version.
221      *
222      * @param policyTypeName the name of policy type
223      * @param policyTypeVersion the version of policy type
224      * @param body the entity body of polic(ies)
225      * @return the ToscaServiceTemplate object containing the policy types that were created
226      * @throws PfModelRuntimeException on errors creating the policy
227      */
228     public ToscaServiceTemplate createPolicy(final String policyTypeName, final String policyTypeVersion,
229         final ToscaServiceTemplate body) throws PfModelRuntimeException {
230         return createPolicies(body);
231     }
232
233     /**
234      * Creates one or more new policies.
235      *
236      * @param body the entity body of policy
237      * @return the ToscaServiceTemplate object containing the policy types that were created
238      * @throws PfModelRuntimeException on errors creating the policy
239      */
240     public ToscaServiceTemplate createPolicies(final ToscaServiceTemplate body) throws PfModelRuntimeException {
241         final var incomingServiceTemplate = new JpaToscaServiceTemplate(body);
242
243         // assert incoming body contains policies
244         ToscaUtils.assertPoliciesExist(incomingServiceTemplate);
245
246         // append the incoming fragment to the DB TOSCA service template
247         final var serviceTemplateToWrite =
248             ToscaServiceTemplateUtils.addFragment(getDefaultJpaToscaServiceTemplate(), incomingServiceTemplate);
249
250         final var result = serviceTemplateToWrite.validate("Policies CRUD service template.");
251         if (!result.isValid()) {
252             throw new PfModelRuntimeException(Response.Status.NOT_ACCEPTABLE, result.getResult());
253         }
254
255         toscaServiceTemplateRepository.save(serviceTemplateToWrite);
256
257         LOGGER.debug("<-appendServiceTemplateFragment: returnServiceTempalate={}", serviceTemplateToWrite);
258         return body;
259     }
260
261     /**
262      * Deletes the policy matching specified name and version of both policy type and policy.
263      *
264      * @param policyTypeName the name of policy type
265      * @param policyTypeVersion the version of policy type
266      * @param policyName the name of policy
267      * @param policyVersion the version of policy
268      * @return the ToscaServiceTemplate object containing the policies that were deleted
269      * @throws PfModelRuntimeException on errors deleting the policy
270      */
271     public ToscaServiceTemplate deletePolicy(final String policyTypeName, final String policyTypeVersion,
272         final String policyName, final String policyVersion) throws PfModelRuntimeException {
273         final var policyKey = new PfConceptKey(policyName, policyVersion);
274         LOGGER.debug("->deletePolicy: name={}, version={}", policyName, policyVersion);
275
276         // terminate if deployed in a PdpGroup
277         pdpGroupService.assertPolicyNotDeployedInPdpGroup(policyName, policyVersion);
278
279         final var serviceTemplate = getDefaultJpaToscaServiceTemplate();
280
281         // terminate deletion if not found
282         if (!ToscaUtils.doPoliciesExist(serviceTemplate)) {
283             throw new PfModelRuntimeException(Response.Status.NOT_FOUND, "no policies found");
284         }
285
286         final var policyForDeletion = serviceTemplate.getTopologyTemplate().getPolicies().get(policyKey);
287         if (policyForDeletion == null) {
288             throw new PfModelRuntimeException(Response.Status.NOT_FOUND, "policy " + policyKey.getId() + NOT_FOUND);
289         }
290
291         // remove policy from service template and write to DB
292         serviceTemplate.getTopologyTemplate().getPolicies().getConceptMap().remove(policyKey);
293         toscaServiceTemplateRepository.save(serviceTemplate);
294
295         // remove the entry from the Policy table
296         policyService.deletePolicy(policyKey);
297
298         // prepare return service template object
299         var deletedServiceTemplate = new JpaToscaServiceTemplate();
300         deletedServiceTemplate.setTopologyTemplate(new JpaToscaTopologyTemplate());
301         deletedServiceTemplate.getTopologyTemplate().setPolicies(new JpaToscaPolicies());
302         deletedServiceTemplate.getTopologyTemplate().getPolicies().getConceptMap().put(policyKey, policyForDeletion);
303
304         LOGGER.debug("<-deletePolicy: key={}, serviceTemplate={}", policyKey, deletedServiceTemplate);
305         return deletedServiceTemplate.toAuthorative();
306     }
307
308     /**
309      * Retrieves TOSCA service template with the specified version of the policy type.
310      *
311      * @param policyTypeName the name of the policy type
312      * @param policyTypeVersion the version of the policy type
313      * @return the TOSCA service template containing the specified version of the policy type
314      * @throws PfModelException on errors getting the policy type
315      */
316     private ToscaServiceTemplate getFilteredPolicyTypes(final String policyTypeName, final String policyTypeVersion)
317         throws PfModelException {
318         final var dbServiceTemplate = getDefaultJpaToscaServiceTemplate();
319         final var policyTypeFilter =
320             ToscaEntityFilter.<ToscaPolicyType>builder().name(policyTypeName).version(policyTypeVersion).build();
321         LOGGER.debug("->getFilteredPolicyTypes: filter={}, serviceTemplate={}", policyTypeFilter, dbServiceTemplate);
322
323         // validate that policyTypes exist in db
324         if (!ToscaUtils.doPolicyTypesExist(dbServiceTemplate)) {
325             throw new PfModelRuntimeException(Response.Status.NOT_FOUND,
326                 "policy types for filter " + policyTypeFilter + DO_NOT_EXIST_MSG);
327         }
328
329         // fetch all polices and filter by policyType, policy name and version
330         final var serviceTemplate = new SimpleToscaProvider()
331             .getCascadedPolicyTypes(dbServiceTemplate, policyTypeName, policyTypeVersion);
332         var simpleToscaProvider = new SimpleToscaProvider();
333
334         List<ToscaPolicyType> filteredPolicyTypes = serviceTemplate.getPolicyTypes().toAuthorativeList();
335         filteredPolicyTypes = policyTypeFilter.filter(filteredPolicyTypes);
336
337         // validate that filtered policyTypes exist
338         if (CollectionUtils.isEmpty(filteredPolicyTypes)) {
339             throw new PfModelRuntimeException(Response.Status.NOT_FOUND,
340                 "policy types for filter " + policyTypeFilter + DO_NOT_EXIST_MSG);
341         }
342
343         // prepare return service template object
344         var returnServiceTemplate = new JpaToscaServiceTemplate();
345         for (var policyType : filteredPolicyTypes) {
346             final var cascadedServiceTemplate = simpleToscaProvider
347                 .getCascadedPolicyTypes(dbServiceTemplate, policyType.getName(), policyType.getVersion());
348             returnServiceTemplate =
349                 ToscaServiceTemplateUtils.addFragment(returnServiceTemplate, cascadedServiceTemplate);
350         }
351
352         LOGGER.debug("<-getFilteredPolicyTypes: filter={}, serviceTemplate={}", policyTypeFilter,
353             returnServiceTemplate);
354         return returnServiceTemplate.toAuthorative();
355
356     }
357
358     /**
359      * Retrieves TOSCA service template with the specified version of the policy.
360      *
361      * @param policyName the name of the policy
362      * @param policyVersion the version of the policy
363      * @param mode the fetch mode for policies
364      * @return the TOSCA service template containing the specified version of the policy
365      * @throws PfModelException on errors getting the policy
366      */
367     private ToscaServiceTemplate getFilteredPolicies(final String policyTypeName, final String policyTypeVersion,
368         final String policyName, final String policyVersion, final PolicyFetchMode mode) throws PfModelException {
369         final var policyFilter = ToscaTypedEntityFilter.<ToscaPolicy>builder()
370             .name(policyName).version(policyVersion).type(policyTypeName).typeVersion(policyTypeVersion).build();
371         final var dbServiceTemplate = getDefaultJpaToscaServiceTemplate();
372         LOGGER.debug("<-getFilteredPolicies: filter={}, serviceTemplate={}", policyFilter, dbServiceTemplate);
373
374         // validate that policies exist in db
375         if (!ToscaUtils.doPolicyTypesExist(dbServiceTemplate)) {
376             throw new PfModelRuntimeException(Response.Status.NOT_FOUND,
377                 "policies for filter " + policyFilter + DO_NOT_EXIST_MSG);
378         }
379
380         final var version =
381             ToscaTypedEntityFilter.LATEST_VERSION.equals(policyFilter.getVersion()) ? null : policyFilter.getVersion();
382
383         // fetch all polices and filter by policyType, policy name and version
384         final var simpleToscaProvider = new SimpleToscaProvider();
385         final var serviceTemplate =
386             simpleToscaProvider.getCascadedPolicies(dbServiceTemplate, policyFilter.getName(), version);
387
388         var filteredPolicies = serviceTemplate.getTopologyTemplate()
389             .getPolicies().toAuthorativeList();
390         filteredPolicies = policyFilter.filter(filteredPolicies);
391
392         // validate that filtered policies exist
393         if (CollectionUtils.isEmpty(filteredPolicies)) {
394             throw new PfModelRuntimeException(Response.Status.NOT_FOUND,
395                 "policies for filter " + policyFilter + DO_NOT_EXIST_MSG);
396         }
397
398         // prepare return service template object
399         var returnServiceTemplate = new JpaToscaServiceTemplate();
400         for (var policy : filteredPolicies) {
401             final var cascadedServiceTemplate = simpleToscaProvider
402                 .getCascadedPolicies(dbServiceTemplate, policy.getName(), policy.getVersion());
403             returnServiceTemplate =
404                 ToscaServiceTemplateUtils.addFragment(returnServiceTemplate, cascadedServiceTemplate);
405         }
406
407         if (mode == null || PolicyFetchMode.BARE.equals(mode)) {
408             returnServiceTemplate.setPolicyTypes(null);
409             returnServiceTemplate.setDataTypes(null);
410         }
411         LOGGER.debug("<-getFilteredPolicies: filter={}, , serviceTemplate={}", policyFilter, returnServiceTemplate);
412         return returnServiceTemplate.toAuthorative();
413     }
414
415     /**
416      * Get Service Template.
417      * @return the Service Template read from the database
418      */
419     private JpaToscaServiceTemplate getDefaultJpaToscaServiceTemplate() {
420         final var defaultServiceTemplateOpt = toscaServiceTemplateRepository
421             .findById(new PfConceptKey(JpaToscaServiceTemplate.DEFAULT_NAME, JpaToscaServiceTemplate.DEFAULT_VERSION));
422         if (defaultServiceTemplateOpt.isEmpty()) {
423             throw new PfModelRuntimeException(Response.Status.NOT_FOUND, SERVICE_TEMPLATE_NOT_FOUND_MSG);
424         }
425         LOGGER.debug("<-getDefaultJpaToscaServiceTemplate: serviceTemplate={}", defaultServiceTemplateOpt.get());
426         return defaultServiceTemplateOpt.get();
427     }
428 }