Update of EI Data Producer API
[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     @ApiOperation(value = "Individual EI type", notes = "")
116     public ResponseEntity<Object> putEiType( //
117         @PathVariable("eiTypeId") String eiTypeId, @RequestBody ProducerEiTypeInfo registrationInfo) {
118
119         EiType previousDefinition = this.eiTypes.get(eiTypeId);
120         this.eiTypes.put(new EiType(eiTypeId, registrationInfo.jobDataSchema));
121         return new ResponseEntity<>(previousDefinition == null ? HttpStatus.CREATED : HttpStatus.OK);
122     }
123
124     @DeleteMapping(path = ProducerConsts.API_ROOT + "/eitypes/{eiTypeId}", produces = MediaType.APPLICATION_JSON_VALUE)
125     @ApiOperation(value = "Individual EI type", notes = "")
126     @ApiResponses(
127         value = { //
128             @ApiResponse(code = 200, message = "Not used", response = VoidResponse.class),
129             @ApiResponse(code = 204, message = "Producer deleted", response = VoidResponse.class),
130             @ApiResponse(
131                 code = 404,
132                 message = "Enrichment Information type is not found",
133                 response = ErrorResponse.ErrorInfo.class),
134             @ApiResponse(
135                 code = 406,
136                 message = "The Enrichment Information type has one or several active producers",
137                 response = ErrorResponse.ErrorInfo.class)})
138     public ResponseEntity<Object> deleteEiType( //
139         @PathVariable("eiTypeId") String eiTypeId) {
140
141         EiType type = this.eiTypes.get(eiTypeId);
142         if (type == null) {
143             return ErrorResponse.create("EI type not found", HttpStatus.NOT_FOUND);
144         }
145         if (!this.eiProducers.getProducersForType(type).isEmpty()) {
146             String firstProducerId = this.eiProducers.getProducersForType(type).iterator().next().getId();
147             return ErrorResponse.create("The type has active producers: " + firstProducerId, HttpStatus.NOT_ACCEPTABLE);
148         }
149         this.eiTypes.remove(type);
150         return new ResponseEntity<>(HttpStatus.NO_CONTENT);
151     }
152
153     @GetMapping(path = ProducerConsts.API_ROOT + "/eiproducers", produces = MediaType.APPLICATION_JSON_VALUE)
154     @ApiOperation(value = "EI producer identifiers", notes = "")
155     @ApiResponses(
156         value = { //
157             @ApiResponse(
158                 code = 200,
159                 message = "EI producer identifiers",
160                 response = String.class,
161                 responseContainer = "List"), //
162         })
163     public ResponseEntity<Object> getEiProducerIdentifiers( //
164         @ApiParam(
165             name = "ei_type_id",
166             required = false,
167             value = "If given, only the producers for the EI Data type is returned.") //
168         @RequestParam(name = "ei_type_id", required = false) String typeId //
169     ) {
170         List<String> result = new ArrayList<>();
171         for (EiProducer eiProducer : typeId == null ? this.eiProducers.getAllProducers()
172             : this.eiProducers.getProducersForType(typeId)) {
173             result.add(eiProducer.getId());
174         }
175
176         return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
177     }
178
179     @GetMapping(
180         path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}",
181         produces = MediaType.APPLICATION_JSON_VALUE)
182     @ApiOperation(value = "Individual EI producer", notes = "")
183     @ApiResponses(
184         value = { //
185             @ApiResponse(code = 200, message = "EI jobs", response = ProducerRegistrationInfo.class), //
186             @ApiResponse(
187                 code = 404,
188                 message = "Enrichment Information producer is not found",
189                 response = ErrorResponse.ErrorInfo.class)})
190     public ResponseEntity<Object> getEiProducer( //
191         @PathVariable("eiProducerId") String eiProducerId) {
192         try {
193             EiProducer p = this.eiProducers.getProducer(eiProducerId);
194             ProducerRegistrationInfo info = toEiProducerRegistrationInfo(p);
195             return new ResponseEntity<>(gson.toJson(info), HttpStatus.OK);
196         } catch (Exception e) {
197             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
198         }
199     }
200
201     @GetMapping(
202         path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}/eijobs",
203         produces = MediaType.APPLICATION_JSON_VALUE)
204     @ApiOperation(value = "EI job definitions", notes = "EI job definitions for one EI producer")
205     @ApiResponses(
206         value = { //
207             @ApiResponse(code = 200, message = "EI jobs", response = ProducerJobInfo.class, responseContainer = "List"), //
208             @ApiResponse(
209                 code = 404,
210                 message = "Enrichment Information producer is not found",
211                 response = ErrorResponse.ErrorInfo.class)})
212     public ResponseEntity<Object> getEiProducerJobs( //
213         @PathVariable("eiProducerId") String eiProducerId) {
214         try {
215             EiProducer producer = this.eiProducers.getProducer(eiProducerId);
216             Collection<ProducerJobInfo> producerJobs = new ArrayList<>();
217             for (EiType type : producer.getEiTypes()) {
218                 for (EiJob eiJob : this.eiJobs.getJobsForType(type)) {
219                     ProducerJobInfo request = new ProducerJobInfo(eiJob);
220                     producerJobs.add(request);
221                 }
222             }
223
224             return new ResponseEntity<>(gson.toJson(producerJobs), HttpStatus.OK);
225         } catch (Exception e) {
226             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
227         }
228     }
229
230     @GetMapping(
231         path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}/status",
232         produces = MediaType.APPLICATION_JSON_VALUE)
233     @ApiOperation(value = "EI producer status")
234     @ApiResponses(
235         value = { //
236             @ApiResponse(code = 200, message = "EI jobs", response = ProducerStatusInfo.class), //
237             @ApiResponse(
238                 code = 404,
239                 message = "Enrichment Information producer is not found",
240                 response = ErrorResponse.ErrorInfo.class)})
241     public ResponseEntity<Object> getEiProducerStatus( //
242         @PathVariable("eiProducerId") String eiProducerId) {
243         try {
244             EiProducer producer = this.eiProducers.getProducer(eiProducerId);
245             return new ResponseEntity<>(gson.toJson(producerStatusInfo(producer)), HttpStatus.OK);
246         } catch (Exception e) {
247             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
248         }
249     }
250
251     private ProducerStatusInfo producerStatusInfo(EiProducer producer) {
252         ProducerStatusInfo.OperationalState opState =
253             producer.isAvailable() ? ProducerStatusInfo.OperationalState.ENABLED
254                 : ProducerStatusInfo.OperationalState.DISABLED;
255         return new ProducerStatusInfo(opState);
256     }
257
258     @PutMapping(
259         path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}",
260         produces = MediaType.APPLICATION_JSON_VALUE)
261     @ApiOperation(value = "Individual EI producer", notes = "")
262     @ApiResponses(
263         value = { //
264             @ApiResponse(code = 201, message = "Producer created", response = VoidResponse.class), //
265             @ApiResponse(code = 200, message = "Producer updated", response = VoidResponse.class)}//
266     )
267     public ResponseEntity<Object> putEiProducer( //
268         @PathVariable("eiProducerId") String eiProducerId, //
269         @RequestBody ProducerRegistrationInfo registrationInfo) {
270         try {
271             EiProducer previousDefinition = this.eiProducers.get(eiProducerId);
272             this.eiProducers.registerProducer(toEiProducerRegistrationInfo(eiProducerId, registrationInfo));
273             return new ResponseEntity<>(previousDefinition == null ? HttpStatus.CREATED : HttpStatus.OK);
274         } catch (Exception e) {
275             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
276         }
277     }
278
279     @DeleteMapping(
280         path = ProducerConsts.API_ROOT + "/eiproducers/{eiProducerId}",
281         produces = MediaType.APPLICATION_JSON_VALUE)
282     @ApiOperation(value = "Individual EI producer", notes = "")
283     @ApiResponses(
284         value = { //
285             @ApiResponse(code = 200, message = "Not used", response = VoidResponse.class),
286             @ApiResponse(code = 204, message = "Producer deleted", response = VoidResponse.class),
287             @ApiResponse(code = 404, message = "Producer is not found", response = ErrorResponse.ErrorInfo.class)})
288     public ResponseEntity<Object> deleteEiProducer(@PathVariable("eiProducerId") String eiProducerId) {
289         try {
290             final EiProducer producer = this.eiProducers.getProducer(eiProducerId);
291             this.eiProducers.deregisterProducer(producer);
292             return new ResponseEntity<>(HttpStatus.NO_CONTENT);
293         } catch (Exception e) {
294             return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
295         }
296     }
297
298     private ProducerRegistrationInfo toEiProducerRegistrationInfo(EiProducer p) {
299         Collection<String> types = new ArrayList<>();
300         for (EiType type : p.getEiTypes()) {
301             types.add(type.getId());
302         }
303         return new ProducerRegistrationInfo(types, p.getJobCallbackUrl(), p.getProducerSupervisionCallbackUrl());
304     }
305
306     private ProducerEiTypeInfo toEiTypeInfo(EiType t) {
307         return new ProducerEiTypeInfo(t.getJobDataSchema());
308     }
309
310     private EiProducers.EiProducerRegistrationInfo toEiProducerRegistrationInfo(String eiProducerId,
311         ProducerRegistrationInfo info) throws ServiceException {
312         Collection<EiType> supportedTypes = new ArrayList<>();
313         for (String typeId : info.supportedTypeIds) {
314             EiType type = this.eiTypes.getType(typeId);
315             supportedTypes.add(type);
316         }
317
318         return ImmutableEiProducerRegistrationInfo.builder() //
319             .id(eiProducerId) //
320             .jobCallbackUrl(info.jobCallbackUrl) //
321             .producerSupervisionCallbackUrl(info.producerSupervisionCallbackUrl) //
322             .supportedTypes(supportedTypes) //
323             .build();
324     }
325
326 }