7e905dab5973c79f2d2db5e55ac59c45223513a3
[nonrtric.git] / enrichment-coordinator-service / src / main / java / org / oransc / enrichment / controllers / producer / ProducerController.java
1 /*-
2  * ========================LICENSE_START=================================
3  * O-RAN-SC
4  * %%
5  * Copyright (C) 2020 Nordix Foundation
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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===================================
19  */
20
21 package org.oransc.enrichment.controllers.producer;
22
23 import com.google.gson.Gson;
24 import com.google.gson.GsonBuilder;
25
26 import io.swagger.annotations.Api;
27 import io.swagger.annotations.ApiOperation;
28 import io.swagger.annotations.ApiParam;
29 import io.swagger.annotations.ApiResponse;
30 import io.swagger.annotations.ApiResponses;
31
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.List;
35
36 import org.oransc.enrichment.controllers.ErrorResponse;
37 import org.oransc.enrichment.controllers.VoidResponse;
38 import org.oransc.enrichment.exceptions.ServiceException;
39 import org.oransc.enrichment.repository.EiJob;
40 import org.oransc.enrichment.repository.EiJobs;
41 import org.oransc.enrichment.repository.EiProducer;
42 import org.oransc.enrichment.repository.EiProducers;
43 import org.oransc.enrichment.repository.EiType;
44 import org.oransc.enrichment.repository.EiTypes;
45 import org.oransc.enrichment.repository.ImmutableEiProducerRegistrationInfo;
46 import org.springframework.beans.factory.annotation.Autowired;
47 import org.springframework.http.HttpStatus;
48 import org.springframework.http.MediaType;
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.PathVariable;
53 import org.springframework.web.bind.annotation.PutMapping;
54 import org.springframework.web.bind.annotation.RequestBody;
55 import org.springframework.web.bind.annotation.RequestParam;
56 import org.springframework.web.bind.annotation.RestController;
57
58 @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
59 @RestController("ProducerController")
60 @Api(tags = {ProducerConsts.PRODUCER_API_NAME})
61 public class ProducerController {
62
63     private static Gson gson = new GsonBuilder().create();
64
65     @Autowired
66     private EiJobs eiJobs;
67
68     @Autowired
69     private EiTypes eiTypes;
70
71     @Autowired
72     private EiProducers eiProducers;
73
74     @GetMapping(path = ProducerConsts.API_ROOT + "/eitypes", produces = MediaType.APPLICATION_JSON_VALUE)
75     @ApiOperation(value = "EI type identifiers", notes = "")
76     @ApiResponses(
77         value = { //
78             @ApiResponse(
79                 code = 200,
80                 message = "EI type identifiers",
81                 response = String.class,
82                 responseContainer = "List"), //
83         })
84     public ResponseEntity<Object> getEiTypeIdentifiers( //
85     ) {
86         List<String> result = new ArrayList<>();
87         for (EiType eiType : this.eiTypes.getAllEiTypes()) {
88             result.add(eiType.getId());
89         }
90
91         return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
92     }
93
94     @GetMapping(path = ProducerConsts.API_ROOT + "/eitypes/{eiTypeId}", produces = MediaType.APPLICATION_JSON_VALUE)
95     @ApiOperation(value = "Individual EI type", notes = "")
96     @ApiResponses(
97         value = { //
98             @ApiResponse(code = 200, message = "EI type", response = ProducerEiTypeInfo.class), //
99             @ApiResponse(
100                 code = 404,
101                 message = "Enrichment Information type is not found",
102                 response = ErrorResponse.ErrorInfo.class)})
103     public ResponseEntity<Object> getEiType( //
104         @PathVariable("eiTypeId") String eiTypeId) {
105         try {
106             EiType t = this.eiTypes.getType(eiTypeId);
107             ProducerEiTypeInfo info = toEiTypeInfo(t);
108             return new ResponseEntity<>(gson.toJson(info), HttpStatus.OK);
109         } catch (Exception e) {
110             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
111         }
112     }
113
114     @PutMapping(path = ProducerConsts.API_ROOT + "/eitypes/{eiTypeId}", produces = MediaType.APPLICATION_JSON_VALUE)
115     @ApiResponses(
116         value = { //
117             @ApiResponse(code = 200, message = "Type updated", response = VoidResponse.class), //
118             @ApiResponse(code = 201, message = "Type created", response = VoidResponse.class), //
119             @ApiResponse(code = 400, message = "Bad request", response = ErrorResponse.ErrorInfo.class)})
120
121     @ApiOperation(value = "Individual EI type", notes = "")
122     public ResponseEntity<Object> putEiType( //
123         @PathVariable("eiTypeId") String eiTypeId, @RequestBody ProducerEiTypeInfo registrationInfo) {
124
125         EiType previousDefinition = this.eiTypes.get(eiTypeId);
126         if (registrationInfo.jobDataSchema == null) {
127             return ErrorResponse.create("No schema provided", HttpStatus.BAD_REQUEST);
128         }
129         this.eiTypes.put(new EiType(eiTypeId, registrationInfo.jobDataSchema));
130         return new ResponseEntity<>(previousDefinition == null ? HttpStatus.CREATED : HttpStatus.OK);
131     }
132
133     @DeleteMapping(path = ProducerConsts.API_ROOT + "/eitypes/{eiTypeId}", produces = MediaType.APPLICATION_JSON_VALUE)
134     @ApiOperation(value = "Individual EI type", notes = "")
135     @ApiResponses(
136         value = { //
137             @ApiResponse(code = 200, message = "Not used", response = VoidResponse.class),
138             @ApiResponse(code = 204, message = "Producer deleted", response = VoidResponse.class),
139             @ApiResponse(
140                 code = 404,
141                 message = "Enrichment Information type is not found",
142                 response = ErrorResponse.ErrorInfo.class),
143             @ApiResponse(
144                 code = 406,
145                 message = "The Enrichment Information type has one or several active producers",
146                 response = ErrorResponse.ErrorInfo.class)})
147     public ResponseEntity<Object> deleteEiType( //
148         @PathVariable("eiTypeId") String eiTypeId) {
149
150         EiType type = this.eiTypes.get(eiTypeId);
151         if (type == null) {
152             return ErrorResponse.create("EI type not found", HttpStatus.NOT_FOUND);
153         }
154         if (!this.eiProducers.getProducersForType(type).isEmpty()) {
155             String firstProducerId = this.eiProducers.getProducersForType(type).iterator().next().getId();
156             return ErrorResponse.create("The type has active producers: " + firstProducerId, HttpStatus.NOT_ACCEPTABLE);
157         }
158         this.eiTypes.remove(type);
159         return new ResponseEntity<>(HttpStatus.NO_CONTENT);
160     }
161
162     @GetMapping(path = ProducerConsts.API_ROOT + "/eiproducers", produces = MediaType.APPLICATION_JSON_VALUE)
163     @ApiOperation(value = "EI producer identifiers", notes = "")
164     @ApiResponses(
165         value = { //
166             @ApiResponse(
167                 code = 200,
168                 message = "EI producer identifiers",
169                 response = String.class,
170                 responseContainer = "List"), //
171         })
172     public ResponseEntity<Object> getEiProducerIdentifiers( //
173         @ApiParam(
174             name = "ei_type_id",
175             required = false,
176             value = "If given, only the producers for the EI Data type is returned.") //
177         @RequestParam(name = "ei_type_id", required = false) String typeId //
178     ) {
179         List<String> result = new ArrayList<>();
180         for (EiProducer eiProducer : typeId == null ? this.eiProducers.getAllProducers()
181             : this.eiProducers.getProducersForType(typeId)) {
182             result.add(eiProducer.getId());
183         }
184
185         return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
186     }
187
188     @GetMapping(
189         path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}",
190         produces = MediaType.APPLICATION_JSON_VALUE)
191     @ApiOperation(value = "Individual EI producer", notes = "")
192     @ApiResponses(
193         value = { //
194             @ApiResponse(code = 200, message = "EI producer", response = ProducerRegistrationInfo.class), //
195             @ApiResponse(
196                 code = 404,
197                 message = "Enrichment Information producer is not found",
198                 response = ErrorResponse.ErrorInfo.class)})
199     public ResponseEntity<Object> getEiProducer( //
200         @PathVariable("eiProducerId") String eiProducerId) {
201         try {
202             EiProducer p = this.eiProducers.getProducer(eiProducerId);
203             ProducerRegistrationInfo info = toEiProducerRegistrationInfo(p);
204             return new ResponseEntity<>(gson.toJson(info), HttpStatus.OK);
205         } catch (Exception e) {
206             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
207         }
208     }
209
210     @GetMapping(
211         path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}/eijobs",
212         produces = MediaType.APPLICATION_JSON_VALUE)
213     @ApiOperation(value = "EI job definitions", notes = "EI job definitions for one EI producer")
214     @ApiResponses(
215         value = { //
216             @ApiResponse(
217                 code = 200,
218                 message = "EI producer",
219                 response = ProducerJobInfo.class,
220                 responseContainer = "List"), //
221             @ApiResponse(
222                 code = 404,
223                 message = "Enrichment Information producer is not found",
224                 response = ErrorResponse.ErrorInfo.class)})
225     public ResponseEntity<Object> getEiProducerJobs( //
226         @PathVariable("eiProducerId") String eiProducerId) {
227         try {
228             EiProducer producer = this.eiProducers.getProducer(eiProducerId);
229             Collection<ProducerJobInfo> producerJobs = new ArrayList<>();
230             for (EiType type : producer.getEiTypes()) {
231                 for (EiJob eiJob : this.eiJobs.getJobsForType(type)) {
232                     ProducerJobInfo request = new ProducerJobInfo(eiJob);
233                     producerJobs.add(request);
234                 }
235             }
236
237             return new ResponseEntity<>(gson.toJson(producerJobs), HttpStatus.OK);
238         } catch (Exception e) {
239             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
240         }
241     }
242
243     @GetMapping(
244         path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}/status",
245         produces = MediaType.APPLICATION_JSON_VALUE)
246     @ApiOperation(value = "EI producer status")
247     @ApiResponses(
248         value = { //
249             @ApiResponse(code = 200, message = "EI producer status", response = ProducerStatusInfo.class), //
250             @ApiResponse(
251                 code = 404,
252                 message = "Enrichment Information producer is not found",
253                 response = ErrorResponse.ErrorInfo.class)})
254     public ResponseEntity<Object> getEiProducerStatus( //
255         @PathVariable("eiProducerId") String eiProducerId) {
256         try {
257             EiProducer producer = this.eiProducers.getProducer(eiProducerId);
258             return new ResponseEntity<>(gson.toJson(producerStatusInfo(producer)), HttpStatus.OK);
259         } catch (Exception e) {
260             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
261         }
262     }
263
264     private ProducerStatusInfo producerStatusInfo(EiProducer producer) {
265         ProducerStatusInfo.OperationalState opState =
266             producer.isAvailable() ? ProducerStatusInfo.OperationalState.ENABLED
267                 : ProducerStatusInfo.OperationalState.DISABLED;
268         return new ProducerStatusInfo(opState);
269     }
270
271     @PutMapping(
272         path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}", //
273         produces = MediaType.APPLICATION_JSON_VALUE)
274     @ApiOperation(value = "Individual EI producer", notes = "")
275     @ApiResponses(
276         value = { //
277             @ApiResponse(code = 201, message = "Producer created", response = VoidResponse.class), //
278             @ApiResponse(code = 200, message = "Producer updated", response = VoidResponse.class)} //
279     )
280     public ResponseEntity<Object> putEiProducer( //
281         @PathVariable("eiProducerId") String eiProducerId, //
282         @RequestBody ProducerRegistrationInfo registrationInfo) {
283         try {
284             EiProducer previousDefinition = this.eiProducers.get(eiProducerId);
285             this.eiProducers.registerProducer(toEiProducerRegistrationInfo(eiProducerId, registrationInfo));
286             return new ResponseEntity<>(previousDefinition == null ? HttpStatus.CREATED : HttpStatus.OK);
287         } catch (Exception e) {
288             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
289         }
290     }
291
292     @DeleteMapping(
293         path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}",
294         produces = MediaType.APPLICATION_JSON_VALUE)
295     @ApiOperation(value = "Individual EI producer", notes = "")
296     @ApiResponses(
297         value = { //
298             @ApiResponse(code = 200, message = "Not used", response = VoidResponse.class),
299             @ApiResponse(code = 204, message = "Producer deleted", response = VoidResponse.class),
300             @ApiResponse(code = 404, message = "Producer is not found", response = ErrorResponse.ErrorInfo.class)})
301     public ResponseEntity<Object> deleteEiProducer(@PathVariable("eiProducerId") String eiProducerId) {
302         try {
303             final EiProducer producer = this.eiProducers.getProducer(eiProducerId);
304             this.eiProducers.deregisterProducer(producer);
305             return new ResponseEntity<>(HttpStatus.NO_CONTENT);
306         } catch (Exception e) {
307             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
308         }
309     }
310
311     private ProducerRegistrationInfo toEiProducerRegistrationInfo(EiProducer p) {
312         Collection<String> types = new ArrayList<>();
313         for (EiType type : p.getEiTypes()) {
314             types.add(type.getId());
315         }
316         return new ProducerRegistrationInfo(types, p.getJobCallbackUrl(), p.getProducerSupervisionCallbackUrl());
317     }
318
319     private ProducerEiTypeInfo toEiTypeInfo(EiType t) {
320         return new ProducerEiTypeInfo(t.getJobDataSchema());
321     }
322
323     private EiProducers.EiProducerRegistrationInfo toEiProducerRegistrationInfo(String eiProducerId,
324         ProducerRegistrationInfo info) throws ServiceException {
325         Collection<EiType> supportedTypes = new ArrayList<>();
326         for (String typeId : info.supportedTypeIds) {
327             EiType type = this.eiTypes.getType(typeId);
328             supportedTypes.add(type);
329         }
330
331         return ImmutableEiProducerRegistrationInfo.builder() //
332             .id(eiProducerId) //
333             .jobCallbackUrl(info.jobCallbackUrl) //
334             .producerSupervisionCallbackUrl(info.producerSupervisionCallbackUrl) //
335             .supportedTypes(supportedTypes) //
336             .build();
337     }
338
339 }