07051f2bab8082ce7a3dfd665bb1bed50f4cb100
[policy/gui.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2018 Ericsson. All rights reserved.
4  *  Modifications Copyright (C) 2019-2022 Nordix Foundation.
5  *  Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
6  *  Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
7  * ================================================================================
8  * Licensed 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  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  * SPDX-License-Identifier: Apache-2.0
21  * ============LICENSE_END=========================================================
22  */
23
24 package org.onap.policy.gui.editors.apex.rest.handling;
25
26 import com.google.gson.GsonBuilder;
27 import com.google.gson.JsonObject;
28 import java.util.ArrayList;
29 import java.util.List;
30 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
31 import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
32 import org.onap.policy.apex.model.basicmodel.concepts.AxKeyInfo;
33 import org.onap.policy.apex.model.modelapi.ApexApiResult;
34 import org.onap.policy.apex.model.modelapi.ApexApiResult.Result;
35 import org.onap.policy.apex.model.modelapi.ApexModel;
36 import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
37 import org.onap.policy.gui.editors.apex.rest.handling.bean.BeanModel;
38 import org.onap.policy.gui.editors.apex.rest.handling.plugin.upload.PolicyUploadHandler;
39 import org.slf4j.ext.XLogger;
40 import org.slf4j.ext.XLoggerFactory;
41 import org.springframework.beans.factory.annotation.Autowired;
42 import org.springframework.stereotype.Service;
43
44 /**
45  * This class handles commands on Apex models.
46  */
47 @Service
48 public class ModelHandler implements RestCommandHandler {
49
50     // Get a reference to the logger
51     private static final XLogger LOGGER = XLoggerFactory.getXLogger(ModelHandler.class);
52
53     private final PolicyUploadHandler policyUploadHandler;
54
55     // Recurring string constants
56     private static final String OK = ": OK";
57     private static final String NOT_OK = ": Not OK";
58     private static final String KEY = "key";
59     private static final String NAME = "name";
60     private static final String VERSION = "version";
61     private static final String UUID = "uuid";
62     private static final String DESCRIPTION = "description";
63     private static final String POLICY_KEY = "policyKey";
64     private static final String APEX_KEY_INFO = "apexKeyInfo";
65
66     @Autowired
67     public ModelHandler(PolicyUploadHandler policyUploadHandler) {
68         this.policyUploadHandler = policyUploadHandler;
69     }
70
71     /**
72      * {@inheritDoc}.
73      */
74     @Override
75     public ApexApiResult executeRestCommand(final RestSession session, final RestCommandType commandType,
76         final RestCommand command) {
77         if (!RestCommandType.MODEL.equals(commandType)) {
78             return getUnsupportedCommandResultMessage(session, commandType, command);
79         }
80
81         switch (command) {
82             case ANALYSE:
83                 return analyse(session);
84             case VALIDATE:
85                 return validate(session);
86             case GET_KEY:
87                 return getModelKey(session);
88             case LIST:
89                 return listModel(session);
90             case DOWNLOAD:
91                 return downloadModel(session);
92             case DELETE:
93                 return deleteModel(session);
94             default:
95                 return getUnsupportedCommandResultMessage(session, commandType, command);
96         }
97     }
98
99     /**
100      * {@inheritDoc}.
101      */
102     @Override
103     public ApexApiResult executeRestCommand(final RestSession session, final RestCommandType commandType,
104         final RestCommand command, final String jsonString) {
105         if (!RestCommandType.MODEL.equals(commandType)) {
106             return getUnsupportedCommandResultMessage(session, commandType, command);
107         }
108
109         switch (command) {
110             case LOAD:
111                 return loadFromString(session, jsonString);
112             case CREATE:
113                 return createModel(session, jsonString);
114             case UPDATE:
115                 return updateModel(session, jsonString);
116             case UPLOAD:
117                 return uploadModel(session, jsonString);
118             default:
119                 return getUnsupportedCommandResultMessage(session, commandType, command);
120         }
121     }
122
123     /**
124      * {@inheritDoc}.
125      */
126     @Override
127     public ApexApiResult executeRestCommand(final RestSession session, final RestCommandType commandType,
128         final RestCommand command, final String name, final String version) {
129         return getUnsupportedCommandResultMessage(session, commandType, command);
130     }
131
132     /**
133      * Load the model from a JSON string for this session.
134      *
135      * @param session    the Apex model editing session
136      * @param jsonString the JSON string to be parsed. The returned value(s) will be similar to {@link AxPolicyModel},
137      *                   with merged {@linkplain AxKeyInfo} for the root object.
138      * @return an ApexAPIResult object. If successful then {@link ApexApiResult#isOk()} will return true. Any
139      *         messages/errors can be retrieved using {@link ApexApiResult#getMessages()}
140      */
141     private ApexApiResult loadFromString(final RestSession session, final String jsonString) {
142         LOGGER.entry(jsonString);
143
144         session.editModel();
145
146         ApexApiResult result = session.loadFromString(jsonString);
147
148         session.finishSession(result.isOk());
149
150         LOGGER.exit("Model/Load" + (result.isOk() ? OK : NOT_OK));
151         return result;
152     }
153
154     /**
155      * Analyse the model and return analysis results. If successful the analysis results will be available in the
156      * messages in the result.
157      *
158      * @param session the Apex model editing session
159      * @return an ApexAPIResult object. If successful then {@link ApexApiResult#isOk()} will return true. Any
160      *         messages/errors can be retrieved using {@link ApexApiResult#getMessages()}
161      */
162     private ApexApiResult analyse(final RestSession session) {
163         LOGGER.entry();
164
165         ApexApiResult result = session.getApexModel().analyse();
166
167         LOGGER.exit("Model/Analyse" + (result != null && result.isOk() ? OK : NOT_OK));
168         return result;
169     }
170
171     /**
172      * Validate the model and return validation results. If successful the validation results will be available in the
173      * messages in the result.
174      *
175      * @param session the Apex model editing session
176      * @return an ApexAPIResult object. If successful then {@link ApexApiResult#isOk()} will return true. Any
177      *         messages/errors can be retrieved using {@link ApexApiResult#getMessages()}
178      */
179     private ApexApiResult validate(final RestSession session) {
180         LOGGER.entry();
181
182         ApexApiResult result = session.getApexModel().validate();
183
184         LOGGER.exit("Model/Validate" + (result != null && result.isOk() ? OK : NOT_OK));
185         return result;
186     }
187
188     /**
189      * Creates the new model model for this session.
190      *
191      * @param session    the Apex model editing session
192      * @param jsonString the JSON string to be parsed containing the new model. See {@linkplain BeanModel}
193      * @return an ApexAPIResult object. If successful then {@link ApexApiResult#isOk()} will return true. Any
194      *         messages/errors can be retrieved using {@link ApexApiResult#getMessages()}
195      */
196     private ApexApiResult createModel(final RestSession session, final String jsonString) {
197         LOGGER.entry(jsonString);
198
199         final var jsonbean = RestUtils.getJsonParameters(jsonString, BeanModel.class);
200
201         session.editModel();
202
203         ApexApiResult result = session.getApexModelEdited().createModel(jsonbean.getName(), jsonbean.getVersion(),
204             jsonbean.getUuid(), jsonbean.getDescription());
205
206         session.finishSession(result.isOk());
207
208         LOGGER.exit("Model/Create" + (result.isOk() ? OK : NOT_OK));
209         return result;
210     }
211
212     /**
213      * Update the model for this session.
214      *
215      * @param session    the Apex model editing session
216      * @param jsonString the JSON string to be parsed containing the updated model. See {@linkplain BeanModel}
217      * @return an ApexAPIResult object. If successful then {@link ApexApiResult#isOk()} will return true. Any
218      *         messages/errors can be retrieved using {@link ApexApiResult#getMessages()}
219      */
220     private ApexApiResult updateModel(final RestSession session, final String jsonString) {
221         LOGGER.entry(jsonString);
222
223         final var jsonbean = RestUtils.getJsonParameters(jsonString, BeanModel.class);
224
225         session.editModel();
226
227         ApexApiResult result = session.getApexModelEdited().updateModel(jsonbean.getName(), jsonbean.getVersion(),
228             jsonbean.getUuid(), jsonbean.getDescription());
229
230         session.finishSession(result.isOk());
231
232         LOGGER.exit("Model/Update" + (result.isOk() ? OK : NOT_OK));
233         return result;
234     }
235
236     /**
237      * Gets the key for the model for this session. If successful the model key will be available in the first message
238      * in the result. See {@linkplain AxKey}
239      *
240      * @param session the Apex model editing session
241      * @return an ApexAPIResult object. If successful then {@link ApexApiResult#isOk()} will return true. Any
242      *         messages/errors can be retrieved using {@link ApexApiResult#getMessages()}
243      */
244     private ApexApiResult getModelKey(final RestSession session) {
245         LOGGER.entry();
246
247         ApexApiResult result = session.getApexModel().getModelKey();
248
249         LOGGER.exit("Model/GetKey" + (result != null && result.isOk() ? OK : NOT_OK));
250         return result;
251     }
252
253     /**
254      * Retrieve the model for this session. If successful the model will be available in the first message in the
255      * result. The returned value will be similar to a {@link AxPolicyModel}, with merged {@linkplain AxKeyInfo} for the
256      * root object.
257      *
258      * @param session the Apex model editing session
259      * @return an ApexAPIResult object. If successful then {@link ApexApiResult#isOk()} will return true. Any
260      *         messages/errors can be retrieved using {@link ApexApiResult#getMessages()}
261      */
262     private ApexApiResult listModel(final RestSession session) {
263         LOGGER.entry();
264
265         ApexApiResult result = session.getApexModel().listModel();
266
267         result = addKeyInfo2Messages(session, result);
268
269         LOGGER.exit("Model/Get" + (result.isOk() ? OK : NOT_OK));
270         return result;
271     }
272
273     /**
274      * Download the model for this session as a String.
275      *
276      * @param session the Apex model editing session
277      * @return the model represented as a JSON string. See {@linkplain AxPolicyModel}
278      */
279     private ApexApiResult downloadModel(final RestSession session) {
280         LOGGER.entry();
281
282         ApexApiResult result = session.downloadModel();
283
284         LOGGER.exit("Model/Download" + (result.isOk() ? OK : NOT_OK));
285         return result;
286     }
287
288     /**
289      * Upload the model for this session as a TOSCA service template YAML string to the configured URL.
290      *
291      * @param session the Apex model editing session
292      * @param userId  the userId to use for upload. If blank, the Spring
293      *                config parameter "apex-editor.upload-userid" is used.
294      * @return a result indicating if the upload was successful or not
295      */
296     private ApexApiResult uploadModel(final RestSession session, String userId) {
297         LOGGER.entry();
298
299         // Get the model in TOSCA format
300         ApexApiResult downloadResult = downloadModel(session);
301         if (downloadResult.isNok()) {
302             LOGGER.exit("Model/Upload" + NOT_OK);
303             return downloadResult;
304         }
305         String toscaServiceTemplate = downloadResult.getMessage();
306
307         ApexModel apexModel = session.getApexModel();
308         ApexModel apexModelEdited = session.getApexModelEdited();
309         ApexModel apexModelBeingUploaded = (apexModelEdited == null ? apexModel : apexModelEdited);
310
311         AxArtifactKey policyModelKey = apexModelBeingUploaded.getPolicyModel().getKey();
312
313         String policyModelUUid = apexModelBeingUploaded.getPolicyModel().getKeyInformation().get(policyModelKey)
314             .getUuid().toString();
315
316         var result = policyUploadHandler.doUpload(toscaServiceTemplate, policyModelKey, policyModelUUid, userId);
317
318         LOGGER.exit("Model/Upload" + (result != null && result.isOk() ? OK : NOT_OK));
319         return result;
320     }
321
322     /**
323      * Delete the model for this session.
324      *
325      * @param session the Apex model editing session
326      * @return an ApexAPIResult object. If successful then {@link ApexApiResult#isOk()} will return true. Any
327      *         messages/errors can be retrieved using {@link ApexApiResult#getMessages()}
328      */
329     private ApexApiResult deleteModel(final RestSession session) {
330         LOGGER.entry();
331
332         session.editModel();
333
334         ApexApiResult result = session.getApexModel().deleteModel();
335
336         session.finishSession(result.isOk());
337
338         LOGGER.exit("Model/Delete" + (result.isOk() ? OK : NOT_OK));
339         return result;
340     }
341
342     /**
343      * The json strings representing the objects listed, stored in result.messages[], does not contain the
344      * AxKeyInformation for that object. This utility method retrieves the AxKeyInfo for each object and adds it to the
345      * json for the object.
346      *
347      * @param session        the Apex model editing session
348      * @param incomingResult The list result, containing JSON representations of objects stored in its "messages" array
349      * @return The list result, containing JSON augmented representations of objects stored in its "messages" array
350      */
351     private ApexApiResult addKeyInfo2Messages(final RestSession session, final ApexApiResult incomingResult) {
352         final var result = new ApexApiResult(incomingResult.getResult());
353         result.setMessages(incomingResult.getMessages());
354
355         final List<String> messages = incomingResult.getMessages();
356         final List<String> augmentedMessages = new ArrayList<>(messages.size());
357
358         for (final String message : messages) {
359             augmentedMessages.add(addKeyInfo2Message(session, message));
360         }
361         result.setMessages(augmentedMessages);
362
363         if (messages.size() != augmentedMessages.size()) {
364             result.setResult(Result.OTHER_ERROR);
365             result.addMessage("Failed to add KeyInfo to all results. Results are not complete");
366         }
367
368         return result;
369     }
370
371     /**
372      * Augment a message with key information.
373      *
374      * @param session the Apex model editing session
375      * @param message The message to augment
376      * @return the augmented message
377      */
378     private String addKeyInfo2Message(final RestSession session, final String message) {
379         final var gson = new GsonBuilder().serializeNulls().enableComplexMapKeySerialization().create();
380
381         var jsonObject = gson.fromJson(message, JsonObject.class);
382         if (jsonObject == null) {
383             return message;
384         }
385
386         String name = readFieldFromJsonObject(jsonObject, NAME, null);
387         String version = readFieldFromJsonObject(jsonObject, VERSION, null);
388
389         if (name == null && version == null) {
390             var newJsonObject = getSubJsonObject(jsonObject);
391
392             if (newJsonObject != null) {
393                 jsonObject = newJsonObject;
394                 name = readFieldFromJsonObject(jsonObject, NAME, name);
395                 version = readFieldFromJsonObject(jsonObject, VERSION, version);
396             }
397         }
398
399         if (name == null || version == null || !setUuidAndDescription(session, jsonObject, name, version)) {
400             jsonObject.addProperty(UUID, (String) null);
401             jsonObject.addProperty(DESCRIPTION, (String) null);
402         }
403
404         return gson.toJson(jsonObject);
405     }
406
407     /**
408      * Get an embedded JSON object for the given JSON object.
409      *
410      * @param jsonObject the input JSON object
411      * @return the embedded JSON object
412      */
413     private JsonObject getSubJsonObject(JsonObject jsonObject) {
414         if (jsonObject.entrySet() != null && !jsonObject.entrySet().isEmpty()) {
415             return (JsonObject) jsonObject.entrySet().iterator().next().getValue();
416         } else {
417             return null;
418         }
419     }
420
421     /**
422      * Condition a field so its key information can be looked up.
423      *
424      * @param jsonObject the object to query
425      * @param fieldTag   the tag of the field to condition
426      * @param value      the default value of the field to condition
427      * @return field read from the json
428      */
429     private String readFieldFromJsonObject(final JsonObject jsonObject, final String fieldTag, final String value) {
430         String lookedupValue = value;
431
432         if (jsonObject != null && jsonObject.get(KEY) != null && jsonObject.get(KEY).isJsonObject()
433             && jsonObject.getAsJsonObject(KEY).get(fieldTag) != null) {
434             lookedupValue = jsonObject.getAsJsonObject(KEY).get(fieldTag).getAsString();
435         } else if (jsonObject != null && jsonObject.get(POLICY_KEY) != null && jsonObject.get(POLICY_KEY).isJsonObject()
436             && jsonObject.getAsJsonObject(POLICY_KEY).get(fieldTag) != null) {
437             lookedupValue = jsonObject.getAsJsonObject(POLICY_KEY).get(fieldTag).getAsString();
438         }
439         return lookedupValue;
440     }
441
442     /**
443      * Look up the UUID and description in the key information for a concept.
444      *
445      * @param session    the Apex editor session
446      * @param jsonObject the JSON object to place the fields in
447      * @param name       the concept name to look up
448      * @param version    the concept version to look up
449      */
450     private boolean setUuidAndDescription(final RestSession session, JsonObject jsonObject, String name,
451         String version) {
452         // Look up the key information for the name and version
453         var keyInfoJsonObject = lookupKeyInfo(session, name, version);
454         if (keyInfoJsonObject == null || keyInfoJsonObject.get(APEX_KEY_INFO) != null) {
455             return false;
456         }
457
458         if (keyInfoJsonObject.get(APEX_KEY_INFO).getAsJsonObject().get("UUID") != null) {
459             jsonObject.addProperty(UUID,
460                 keyInfoJsonObject.get(APEX_KEY_INFO).getAsJsonObject().get("UUID").getAsString());
461         } else {
462             jsonObject.addProperty(UUID, (String) null);
463         }
464
465         if (keyInfoJsonObject.get(APEX_KEY_INFO).getAsJsonObject().get(DESCRIPTION) != null) {
466             jsonObject.addProperty(DESCRIPTION,
467                 keyInfoJsonObject.get(APEX_KEY_INFO).getAsJsonObject().get(DESCRIPTION).getAsString());
468         } else {
469             jsonObject.addProperty(DESCRIPTION, (String) null);
470         }
471
472         return true;
473     }
474
475     /**
476      * Look up the key information for the given concept name and value.
477      *
478      * @param session the Apex editor session
479      * @param name    the concept name to look up
480      * @param version the concept version to look up
481      * @return a JSON version of the concept key information
482      */
483     private JsonObject lookupKeyInfo(final RestSession session, final String name, final String version) {
484         final ApexApiResult keyInfoResult = session.getApexModel().listKeyInformation(name, version);
485         final List<String> keyInfoMessages = keyInfoResult.getMessages();
486
487         if (keyInfoResult.isNok() || keyInfoMessages == null || keyInfoMessages.isEmpty()) {
488             return null;
489         }
490
491         final var gson = new GsonBuilder().serializeNulls().enableComplexMapKeySerialization().create();
492         final String keyInfoJson = keyInfoMessages.get(0);
493         return gson.fromJson(keyInfoJson, JsonObject.class);
494     }
495 }