2 * ========================LICENSE_START=================================
4 * ======================================================================
5 * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved.
6 * ======================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License 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===================================
21 package org.onap.ccsdk.oran.a1policymanagementservice.controllers.v1;
23 import com.google.gson.Gson;
24 import com.google.gson.GsonBuilder;
26 import io.swagger.v3.oas.annotations.Operation;
27 import io.swagger.v3.oas.annotations.Parameter;
28 import io.swagger.v3.oas.annotations.media.ArraySchema;
29 import io.swagger.v3.oas.annotations.media.Content;
30 import io.swagger.v3.oas.annotations.media.Schema;
31 import io.swagger.v3.oas.annotations.responses.ApiResponse;
32 import io.swagger.v3.oas.annotations.responses.ApiResponses;
33 import io.swagger.v3.oas.annotations.tags.Tag;
35 import java.net.MalformedURLException;
37 import java.time.Duration;
38 import java.util.ArrayList;
39 import java.util.Collection;
41 import org.onap.ccsdk.oran.a1policymanagementservice.controllers.VoidResponse;
42 import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException;
43 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policies;
44 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy;
45 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Service;
46 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Services;
47 import org.springframework.beans.factory.annotation.Autowired;
48 import org.springframework.http.HttpStatus;
49 import org.springframework.http.ResponseEntity;
50 import org.springframework.web.bind.annotation.DeleteMapping;
51 import org.springframework.web.bind.annotation.GetMapping;
52 import org.springframework.web.bind.annotation.PutMapping;
53 import org.springframework.web.bind.annotation.RequestBody;
54 import org.springframework.web.bind.annotation.RequestParam;
55 import org.springframework.web.bind.annotation.RestController;
58 @Tag(name = Consts.V1_API_NAME)
59 public class ServiceController {
61 private final Services services;
62 private final Policies policies;
64 private static Gson gson = new GsonBuilder().create();
67 ServiceController(Services services, Policies policies) {
68 this.services = services;
69 this.policies = policies;
72 @GetMapping("/services")
73 @Operation(summary = "Returns service information")
74 @ApiResponses(value = { //
75 @ApiResponse(responseCode = "200", //
76 description = "OK", //
77 content = @Content(array = @ArraySchema(schema = @Schema(implementation = ServiceStatus.class)))), //
78 @ApiResponse(responseCode = "404", //
79 description = "Service is not found", //
80 content = @Content(schema = @Schema(implementation = String.class))) //
83 public ResponseEntity<String> getServices(//
84 @Parameter(name = "name", required = false, description = "The name of the service") //
85 @RequestParam(name = "name", required = false) String name) {
86 if (name != null && this.services.get(name) == null) {
87 return new ResponseEntity<>("Service not found", HttpStatus.NOT_FOUND);
90 Collection<ServiceStatus> servicesStatus = new ArrayList<>();
91 for (Service s : this.services.getAll()) {
92 if (name == null || name.equals(s.getName())) {
93 servicesStatus.add(toServiceStatus(s));
97 String res = gson.toJson(servicesStatus);
98 return new ResponseEntity<>(res, HttpStatus.OK);
101 private ServiceStatus toServiceStatus(Service s) {
102 return new ServiceStatus(s.getName(), s.getKeepAliveInterval().toSeconds(), s.timeSinceLastPing().toSeconds(),
106 private void validateRegistrationInfo(ServiceRegistrationInfo registrationInfo)
107 throws ServiceException, MalformedURLException {
108 if (registrationInfo.serviceName.isEmpty()) {
109 throw new ServiceException("Missing mandatory parameter 'serviceName'");
111 if (registrationInfo.keepAliveIntervalSeconds < 0) {
112 throw new ServiceException("Keepalive interval should be greater or equal to 0");
114 if (!registrationInfo.callbackUrl.isEmpty()) {
115 new URL(registrationInfo.callbackUrl);
119 @Operation(summary = "Register a service")
120 @ApiResponses(value = { //
121 @ApiResponse(responseCode = "200", //
122 description = "Service updated", //
123 content = @Content(schema = @Schema(implementation = String.class))), //
124 @ApiResponse(responseCode = "201", //
125 description = "Service created", //
126 content = @Content(schema = @Schema(implementation = String.class))), //
127 @ApiResponse(responseCode = "400", //
128 description = "The ServiceRegistrationInfo is not accepted", //
129 content = @Content(schema = @Schema(implementation = String.class))) //
131 @PutMapping("/service")
132 public ResponseEntity<String> putService(//
133 @RequestBody ServiceRegistrationInfo registrationInfo) {
135 validateRegistrationInfo(registrationInfo);
136 final boolean isCreate = this.services.get(registrationInfo.serviceName) == null;
137 this.services.put(toService(registrationInfo));
138 return new ResponseEntity<>("OK", isCreate ? HttpStatus.CREATED : HttpStatus.OK);
139 } catch (Exception e) {
140 return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
144 @Operation(summary = "Unregisters a service")
145 @ApiResponses(value = { //
146 @ApiResponse(responseCode = "204", description = "Service unregisterred", //
147 content = @Content(schema = @Schema(implementation = VoidResponse.class))),
148 @ApiResponse(responseCode = "404", description = "Service not found", //
149 content = @Content(schema = @Schema(implementation = String.class)))})
150 @DeleteMapping("/services")
151 public ResponseEntity<String> deleteService(//
152 @Parameter(name = "name", required = true, description = "The name of the service") //
153 @RequestParam(name = "name", required = true) String serviceName) {
155 Service service = removeService(serviceName);
156 // Remove the policies from the repo and let the consistency monitoring
158 removePolicies(service);
159 return new ResponseEntity<>("OK", HttpStatus.NO_CONTENT);
160 } catch (Exception e) {
161 return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
165 @Operation(summary = "Heartbeat from a service")
166 @ApiResponses(value = { //
167 @ApiResponse(responseCode = "200", description = "Service supervision timer refreshed, OK"),
168 @ApiResponse(responseCode = "404", description = "The service is not found, needs re-registration")
172 @PutMapping("/services/keepalive")
173 public ResponseEntity<String> keepAliveService(//
174 @Parameter(name = "name", required = true, description = "The name of the service") //
175 @RequestParam(name = "name", required = true) String serviceName) {
177 services.getService(serviceName).keepAlive();
178 return new ResponseEntity<>("OK", HttpStatus.OK);
179 } catch (ServiceException e) {
180 return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
184 private Service removeService(String name) throws ServiceException {
185 Service service = this.services.getService(name); // Just to verify that it exists
186 this.services.remove(service.getName());
190 private void removePolicies(Service service) {
191 Collection<Policy> policyList = this.policies.getForService(service.getName());
192 for (Policy policy : policyList) {
193 this.policies.remove(policy);
197 private Service toService(ServiceRegistrationInfo s) {
198 return new Service(s.serviceName, Duration.ofSeconds(s.keepAliveIntervalSeconds), s.callbackUrl);