b50e7a0ed0950193587183232c3ebd71265e0c0d
[policy/clamp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 Nordix Foundation.
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.clamp.controlloop.runtime.main.rest;
22
23 import com.fasterxml.jackson.core.JsonProcessingException;
24 import com.fasterxml.jackson.databind.ObjectMapper;
25 import com.fasterxml.jackson.databind.PropertyNamingStrategies;
26 import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
27 import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
28 import io.swagger.annotations.ApiOperation;
29 import io.swagger.annotations.ApiParam;
30 import io.swagger.annotations.ApiResponse;
31 import io.swagger.annotations.ApiResponses;
32 import io.swagger.annotations.Authorization;
33 import io.swagger.annotations.Extension;
34 import io.swagger.annotations.ExtensionProperty;
35 import io.swagger.annotations.ResponseHeader;
36 import java.util.List;
37 import java.util.UUID;
38 import javax.ws.rs.core.Response.Status;
39 import org.onap.policy.clamp.controlloop.models.messages.rest.commissioning.CommissioningResponse;
40 import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningProvider;
41 import org.onap.policy.clamp.controlloop.runtime.main.web.AbstractRestController;
42 import org.onap.policy.models.base.PfModelException;
43 import org.onap.policy.models.base.PfModelRuntimeException;
44 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
45 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.springframework.http.HttpStatus;
49 import org.springframework.http.MediaType;
50 import org.springframework.http.ResponseEntity;
51 import org.springframework.web.bind.annotation.DeleteMapping;
52 import org.springframework.web.bind.annotation.GetMapping;
53 import org.springframework.web.bind.annotation.PostMapping;
54 import org.springframework.web.bind.annotation.RequestBody;
55 import org.springframework.web.bind.annotation.RequestHeader;
56 import org.springframework.web.bind.annotation.RequestParam;
57 import org.springframework.web.bind.annotation.RestController;
58
59 /**
60  * Class to provide REST end points for creating, deleting, querying commissioned control loops.
61  */
62 @RestController
63 public class CommissioningController extends AbstractRestController {
64
65     private static final Logger LOGGER = LoggerFactory.getLogger(CommissioningController.class);
66
67     private final CommissioningProvider provider;
68
69     /**
70      * Create Commissioning Controller.
71      *
72      * @param provider the CommissioningProvider
73      */
74     public CommissioningController(CommissioningProvider provider) {
75         this.provider = provider;
76     }
77
78     /**
79      * Creates a control loop definition.
80      *
81      * @param requestId request ID used in ONAP logging
82      * @param body the body of control loop following TOSCA definition
83      * @return a response
84      */
85     // @formatter:off
86     @PostMapping(value = "/commission",
87             consumes = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML},
88             produces = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML})
89     @ApiOperation(
90         value = "Commissions control loop definitions",
91         notes = "Commissions control loop definitions, returning the commissioned control loop definition IDs",
92         response = CommissioningResponse.class,
93         tags = {"Control Loop Commissioning API"},
94         authorizations = @Authorization(value = AUTHORIZATION_TYPE),
95         responseHeaders = {
96             @ResponseHeader(
97                 name = VERSION_MINOR_NAME,
98                 description = VERSION_MINOR_DESCRIPTION,
99                 response = String.class),
100             @ResponseHeader(
101                 name = VERSION_PATCH_NAME,
102                 description = VERSION_PATCH_DESCRIPTION,
103                 response = String.class),
104             @ResponseHeader(
105                 name = VERSION_LATEST_NAME,
106                 description = VERSION_LATEST_DESCRIPTION,
107                 response = String.class),
108             @ResponseHeader(
109                 name = REQUEST_ID_NAME,
110                 description = REQUEST_ID_HDR_DESCRIPTION,
111                 response = UUID.class)
112         },
113         extensions = {
114             @Extension
115                 (
116                     name = EXTENSION_NAME,
117                     properties = {
118                         @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
119                         @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
120                     }
121                 )
122         }
123     )
124     @ApiResponses(
125             value = {
126                 @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
127                 @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
128                 @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
129             }
130     )
131     // @formatter:on
132     public ResponseEntity<CommissioningResponse> create(
133             @RequestHeader(
134                     name = REQUEST_ID_NAME,
135                     required = false) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
136             @ApiParam(value = "Entity Body of Control Loop", required = true) @RequestBody ToscaServiceTemplate body) {
137         try {
138             return ResponseEntity.ok().body(provider.createControlLoopDefinitions(body));
139
140         } catch (PfModelRuntimeException | PfModelException e) {
141             LOGGER.warn("Commissioning of the control loops failed", e);
142             var resp = new CommissioningResponse();
143             resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
144             return ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode()).body(resp);
145         }
146
147     }
148
149     /**
150      * Deletes a control loop definition.
151      *
152      * @param requestId request ID used in ONAP logging
153      * @param name the name of the control loop definition to delete
154      * @param version the version of the control loop definition to delete
155      * @return a response
156      */
157     // @formatter:off
158     @DeleteMapping(value = "/commission",
159             produces = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML})
160     @ApiOperation(value = "Delete a commissioned control loop",
161         notes = "Deletes a Commissioned Control Loop, returning optional error details",
162         response = CommissioningResponse.class,
163         tags = {"Clamp Control Loop Commissioning API"},
164         authorizations = @Authorization(value = AUTHORIZATION_TYPE),
165         responseHeaders = {
166             @ResponseHeader(
167                 name = VERSION_MINOR_NAME,
168                 description = VERSION_MINOR_DESCRIPTION,
169                 response = String.class),
170             @ResponseHeader(
171                 name = VERSION_PATCH_NAME,
172                 description = VERSION_PATCH_DESCRIPTION,
173                 response = String.class),
174             @ResponseHeader(
175                 name = VERSION_LATEST_NAME,
176                 description = VERSION_LATEST_DESCRIPTION,
177                 response = String.class),
178             @ResponseHeader(
179                 name = REQUEST_ID_NAME,
180                 description = REQUEST_ID_HDR_DESCRIPTION,
181                 response = UUID.class)},
182         extensions = {
183             @Extension
184                 (
185                     name = EXTENSION_NAME,
186                     properties = {
187                         @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
188                         @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
189                     }
190                 )
191         }
192     )
193     @ApiResponses(
194         value = {
195             @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
196             @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
197             @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
198         }
199     )
200     // @formatter:on
201     public ResponseEntity<CommissioningResponse> delete(
202             @RequestHeader(
203                     name = REQUEST_ID_NAME,
204                     required = false) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
205             @ApiParam(value = "Control Loop definition name", required = true) @RequestParam(
206                     value = "name") String name,
207             @ApiParam(
208                     value = "Control Loop definition version",
209                     required = true) @RequestParam("version") String version) {
210
211         try {
212             return ResponseEntity.ok().body(provider.deleteControlLoopDefinition(name, version));
213
214         } catch (PfModelRuntimeException | PfModelException e) {
215             LOGGER.warn("Decommisssioning of control loop failed", e);
216             var resp = new CommissioningResponse();
217             resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
218             return ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode()).body(resp);
219         }
220
221     }
222
223     /**
224      * Queries details of all or specific control loop definitions.
225      *
226      * @param requestId request ID used in ONAP logging
227      * @param name the name of the control loop definition to get, null for all definitions
228      * @param version the version of the control loop definition to get, null for all definitions
229      * @return the control loop definitions
230      */
231     // @formatter:off
232     @GetMapping(value = "/commission",
233             produces = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML})
234     @ApiOperation(value = "Query details of the requested commissioned control loop definitions",
235         notes = "Queries details of the requested commissioned control loop definitions, "
236             + "returning all control loop details",
237         response = ToscaNodeTemplate.class,
238         tags = {"Clamp Control Loop Commissioning API"},
239         authorizations = @Authorization(value = AUTHORIZATION_TYPE),
240         responseHeaders = {
241             @ResponseHeader(
242                 name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
243                 response = String.class),
244             @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
245                 response = String.class),
246             @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
247                 response = String.class),
248             @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
249                 response = UUID.class)},
250         extensions = {
251             @Extension
252                 (
253                     name = EXTENSION_NAME,
254                     properties = {
255                         @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
256                         @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
257                     }
258                 )
259             }
260     )
261     @ApiResponses(
262         value = {
263             @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
264             @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
265             @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
266         }
267     )
268     // @formatter:on
269     public ResponseEntity<?> query(
270             @RequestHeader(
271                     name = REQUEST_ID_NAME,
272                     required = false) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
273             @ApiParam(value = "Control Loop definition name", required = false) @RequestParam(
274                     value = "name",
275                     required = false) String name,
276             @ApiParam(value = "Control Loop definition version", required = false) @RequestParam(
277                     value = "version",
278                     required = false) String version) {
279
280         try {
281             return ResponseEntity.ok().body(provider.getControlLoopDefinitions(name, version));
282
283         } catch (PfModelRuntimeException | PfModelException e) {
284             LOGGER.warn("Get of control loop definitions failed", e);
285             var resp = new CommissioningResponse();
286             resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
287             return ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode()).body(resp);
288         }
289
290     }
291
292     /**
293      * Retrieves the Tosca Service Template.
294      *
295      * @param requestId request ID used in ONAP logging
296      * @param name the name of the tosca service template to retrieve
297      * @param version the version of the tosca service template to get
298      * @return the specified tosca service template
299      */
300     // @formatter:off
301     @GetMapping(value = "/commission/toscaservicetemplate",
302             produces = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML})
303     @ApiOperation(value = "Query details of the requested tosca service templates",
304         notes = "Queries details of the requested commissioned tosca service template, "
305             + "returning all tosca service template details",
306         response = ToscaServiceTemplate.class,
307         tags = {"Clamp Control Loop Commissioning API"},
308         authorizations = @Authorization(value = AUTHORIZATION_TYPE),
309         responseHeaders = {
310             @ResponseHeader(
311                 name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
312                 response = String.class),
313             @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
314                 response = String.class),
315             @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
316                 response = String.class),
317             @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
318                 response = UUID.class)},
319         extensions = {
320             @Extension
321                 (
322                     name = EXTENSION_NAME,
323                     properties = {
324                         @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
325                         @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
326                     }
327                 )
328         }
329     )
330     @ApiResponses(
331         value = {
332             @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
333             @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
334             @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
335         }
336     )
337     // @formatter:on
338     public ResponseEntity<?> queryToscaServiceTemplate(
339             @RequestHeader(
340                     name = REQUEST_ID_NAME,
341                     required = false) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
342             @ApiParam(value = "Tosca service template name", required = false) @RequestParam(
343                     value = "name",
344                     required = false) String name,
345             @ApiParam(value = "Tosca service template version", required = true) @RequestParam(
346                     value = "version",
347                     required = false) String version) {
348
349         try {
350             return ResponseEntity.ok().body(provider.getToscaServiceTemplate(name, version));
351
352         } catch (PfModelRuntimeException | PfModelException e) {
353             LOGGER.warn("Get of tosca service template failed", e);
354             var resp = new CommissioningResponse();
355             resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
356             return ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode()).body(resp);
357         }
358
359     }
360
361     /**
362      * Retrieves the Json Schema for the specified Tosca Service Template.
363      *
364      * @param requestId request ID used in ONAP logging
365      * @param section section of the tosca service template to get schema for
366      * @return the specified tosca service template or section Json Schema
367      */
368     // @formatter:off
369     @GetMapping(value = "/commission/toscaServiceTemplateSchema",
370         produces = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML})
371     @ApiOperation(value = "Query details of the requested tosca service template json schema",
372         notes = "Queries details of the requested commissioned tosca service template json schema, "
373             + "returning all tosca service template json schema details",
374         response = ToscaServiceTemplate.class,
375         tags = {"Clamp Control Loop Commissioning API"},
376         authorizations = @Authorization(value = AUTHORIZATION_TYPE),
377         responseHeaders = {
378             @ResponseHeader(
379                 name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
380                 response = String.class),
381             @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
382                 response = String.class),
383             @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
384                 response = String.class),
385             @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
386                 response = UUID.class)},
387         extensions = {
388             @Extension
389                 (
390                     name = EXTENSION_NAME,
391                     properties = {
392                         @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
393                         @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
394                     }
395                 )
396         }
397     )
398     @ApiResponses(
399         value = {
400             @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
401             @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
402             @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
403         }
404     )
405     // @formatter:on
406     public ResponseEntity<?> queryToscaServiceTemplateJsonSchema(
407         @RequestHeader(
408             name = REQUEST_ID_NAME,
409             required = false) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
410         @ApiParam(value = "Section of Template schema is desired for", required = false) @RequestParam(
411             value = "section",
412             required = false, defaultValue = "all") String section) {
413         try {
414             return ResponseEntity.ok().body(provider.getToscaServiceTemplateSchema(section));
415
416         } catch (PfModelRuntimeException | PfModelException e) {
417             LOGGER.warn("Get of tosca service template json schema failed", e);
418             var resp = new CommissioningResponse();
419             resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
420             return ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode()).body(resp);
421         } catch (JsonProcessingException e) {
422             LOGGER.warn("Get of tosca service template json schema failed", e);
423             var resp = new CommissioningResponse();
424             resp.setErrorDetails(e.getMessage());
425             return ResponseEntity.status(Status.BAD_REQUEST.getStatusCode()).body(resp);
426         }
427     }
428
429     /**
430      * Queries the elements of a specific control loop.
431      *
432      * @param requestId request ID used in ONAP logging
433      * @param name the name of the control loop definition to get
434      * @param version the version of the control loop definition to get
435      * @return the control loop element definitions
436      */
437     // @formatter:off
438     @GetMapping(value = "/commission/elements",
439             produces = {MediaType.APPLICATION_JSON_VALUE, APPLICATION_YAML})
440     @ApiOperation(value = "Query details of the requested commissioned control loop element definitions",
441         notes = "Queries details of the requested commissioned control loop element definitions, "
442             + "returning all control loop elements' details",
443         response = ToscaNodeTemplate.class,
444         tags = {"Clamp Control Loop Commissioning API"},
445         authorizations = @Authorization(value = AUTHORIZATION_TYPE),
446         responseHeaders = {
447             @ResponseHeader(
448                 name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
449                 response = String.class),
450             @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
451                 response = String.class),
452             @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
453                 response = String.class),
454             @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
455                 response = UUID.class)},
456         extensions = {
457             @Extension
458                 (
459                     name = EXTENSION_NAME,
460                     properties = {
461                         @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
462                         @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
463                     }
464                 )
465         }
466     )
467     @ApiResponses(
468         value = {
469             @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
470             @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
471             @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
472         }
473     )
474     // @formatter:on
475     public ResponseEntity<?> queryElements(
476             @RequestHeader(
477                     name = REQUEST_ID_NAME,
478                     required = false) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
479             @ApiParam(value = "Control Loop definition name", required = false) @RequestParam(
480                     value = "name",
481                     required = false) String name,
482             @ApiParam(value = "Control Loop definition version", required = true) @RequestParam(
483                     value = "version",
484                     required = false) String version) {
485
486         try {
487             List<ToscaNodeTemplate> nodeTemplate = provider.getControlLoopDefinitions(name, version);
488             // Prevent ambiguous queries with multiple returns
489             if (nodeTemplate.size() > 1) {
490                 var resp = new CommissioningResponse();
491                 resp.setErrorDetails("Multiple ControlLoops are not supported");
492                 return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).body(resp);
493             }
494
495             List<ToscaNodeTemplate> response = provider.getControlLoopElementDefinitions(nodeTemplate.get(0));
496             return ResponseEntity.ok().body(response);
497
498         } catch (PfModelRuntimeException | PfModelException e) {
499             LOGGER.warn("Get of control loop element definitions failed", e);
500             var resp = new CommissioningResponse();
501             resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
502             return ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode()).body(resp);
503         }
504
505     }
506 }