[DMAAP-BC] Consolidate bus controller repos
[dmaap/buscontroller.git] / dmaap-bc / src / main / java / org / onap / dmaap / dbcapi / service / FeedService.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * org.onap.dmaap
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  *
7  * Modifications Copyright (C) 2019 IBM.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.dmaap.dbcapi.service;
24
25 import java.util.ArrayList;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import javax.ws.rs.core.Response.Status;
30 import org.json.simple.JSONArray;
31 import org.json.simple.JSONObject;
32 import org.json.simple.parser.JSONParser;
33 import org.json.simple.parser.ParseException;
34 import org.onap.dmaap.dbcapi.client.DrProvConnection;
35 import org.onap.dmaap.dbcapi.database.DatabaseClass;
36 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
37 import org.onap.dmaap.dbcapi.model.ApiError;
38 import org.onap.dmaap.dbcapi.model.DR_Pub;
39 import org.onap.dmaap.dbcapi.model.DR_Sub;
40 import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
41 import org.onap.dmaap.dbcapi.model.Feed;
42 import org.onap.dmaap.dbcapi.util.DmaapConfig;
43 import org.onap.dmaap.dbcapi.util.RandomInteger;
44
45 public class FeedService  extends BaseLoggingClass {
46         
47         private Map<String, Feed> feeds = DatabaseClass.getFeeds();
48         private Map<String, DR_Sub> dr_subs = DatabaseClass.getDr_subs();
49         private DR_PubService pubService = new DR_PubService();
50         private DR_SubService subService = new DR_SubService();
51         private DcaeLocationService dcaeLocations = new DcaeLocationService();
52         private String deleteHandling;
53         private String unit_test;
54         
55         public FeedService() {
56                 logger.info( "new FeedService");
57                 DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
58                 deleteHandling = p.getProperty("Feed.deleteHandling", "DeleteOnDR");
59                 unit_test = p.getProperty( "UnitTest", "No" );
60
61         }
62         
63         public Map<String, Feed> getFeeds() {                   
64                 return feeds;
65         }
66         
67         private void getSubObjects( Feed f ) {
68                 ArrayList<DR_Pub> pubs = pubService.getDr_PubsByFeedId( f.getFeedId() );
69                 f.setPubs(pubs);
70                 ArrayList<DR_Sub> subs = subService.getDr_SubsByFeedId( f.getFeedId() );
71                 f.setSubs(subs);        
72         }
73         
74         public List<Feed> getAllFeeds(){
75                 return getAllFeeds(null, null, null);
76         }
77                 
78         public List<Feed> getAllFeeds( String name, String ver, String match ) {
79                 logger.info( "getAllFeeds: name=" + name + " ver=" + ver + " match=" + match);
80                 ArrayList<Feed> fatFeeds = new ArrayList<Feed>();
81                 for( Feed f:  feeds.values() ) {
82                         boolean keep = true;
83                         if ( name != null ) {
84                                 if ( match != null && "startsWith".equals(match) ) {
85                                         if ( ! f.getFeedName().startsWith( name ) ) {
86                                                 logger.info( "getAllFeeds: feedName=" + f.getFeedName() + " doesn't start with=" + name);
87                                                 keep = false;
88                                         }
89                                 } else if ( match != null && match.equals("contains") ) {
90                                         if ( ! f.getFeedName().contains( name ) ) {
91                                                 logger.info( "getAllFeeds: feedName=" + f.getFeedName() + " doesn't contain=" + name);
92                                                 keep = false;
93                                         }
94                                 } else {
95                                         if ( ! f.getFeedName().equals( name ) ) {
96                                                 logger.info( "getAllFeeds: feedName=" + f.getFeedName() + " doesn't equal=" + name);
97                                                 keep = false;
98                                         }
99                                 }
100
101                         }
102                         if ( keep && ver != null ) {
103                                 if ( ! f.getFeedVersion().equals(ver)) {
104                                         logger.info( "getAllFeeds: feedVersion=" + f.getFeedName() + " doesn't match " + ver);
105                                         keep = false;
106                                 } else {
107                                         logger.info( "getAllFeeds: feedVersion=" + f.getFeedName() + " matches " + ver);
108                                 }
109                         }
110                                         
111                         if (keep){
112                                 getSubObjects(f);
113                                 fatFeeds.add(f);
114                         }
115                 }
116                 return fatFeeds;
117         }
118         
119         
120         private Feed _getFeed( String key, ApiError err, boolean flag ) {
121                 Feed f = feeds.get( key );
122                 if ( f != null && ( flag || f.getStatus() != DmaapObject_Status.DELETED ) ) {
123                         getSubObjects( f );
124                 } else {
125                         err.setCode(Status.NOT_FOUND.getStatusCode());
126                         err.setMessage("feed not found");
127                         err.setFields("feedId=" + key );
128                         return null;
129                 }
130                 err.setCode(200);
131                 return f;
132         }
133         public Feed getFeed( String key, ApiError err ) {
134                 return _getFeed( key, err, false );
135         }
136         public Feed getFeedPure( String key, ApiError err ) {
137                 return _getFeed( key, err, true );
138         }
139         
140         public Feed getFeedByName( String name, String ver, ApiError err ) {
141                 for( Feed f:  feeds.values() ) {
142                         if ( f.getFeedName().equals( name ) && f.getFeedVersion().equals(ver) ) {
143                                 getSubObjects(f);
144                                 return f;
145                         }
146         
147                 }
148                 err.setCode(Status.NOT_FOUND.getStatusCode());
149                 err.setMessage("feed not found");
150                 err.setFields("feedName=" + name + " and ver=" + ver );
151                 return null;
152         
153         }
154
155         private boolean savePubs( Feed f ) {
156                 return savePubs( f, f );
157         }
158         // need to save the Pub objects independently and copy pubId from original request
159         private boolean savePubs( Feed fnew, Feed req ) {
160                 // save any pubs
161                 DR_PubService pubSvc = new DR_PubService();
162                 ArrayList<DR_Pub> reqPubs = req.getPubs();
163                 ArrayList<DR_Pub> newPubs = fnew.getPubs();
164                 
165
166                 
167                 int nSize = newPubs.size();
168                 int rSize = reqPubs.size();
169                 logger.info( "reqPubs size=" + rSize + " newPubs size=" + nSize );
170                 if ( nSize != rSize ) {
171                         errorLogger.error( "Resulting set of publishers do not match requested set of publishers " + nSize + " vs " + rSize );
172                         fnew.setStatus( DmaapObject_Status.INVALID);
173                         return false;
174                 }
175                 // NOTE: when i > 1 newPubs are in reverse order from reqPubs
176                 for( int i = 0; i < reqPubs.size(); i++ ) {
177                         DR_Pub reqPub = reqPubs.get(i); 
178                         ApiError err = new ApiError();
179                         if ( pubSvc.getDr_Pub( reqPub.getPubId(), err ) == null ) {
180                                 DR_Pub newPub = newPubs.get(nSize - i - 1);
181                                 reqPub.setPubId(newPub.getPubId());
182                                 reqPub.setFeedId(newPub.getFeedId());
183                                 reqPub.setStatus(DmaapObject_Status.VALID);
184                                 if ( reqPub.getDcaeLocationName() == null ) {
185                                         reqPub.setDcaeLocationName("notSpecified");
186                                 }
187                                 pubSvc.addDr_Pub( reqPub );
188                         }
189                         
190                 }
191                 
192                 fnew.setPubs(reqPubs);
193                 fnew.setStatus(DmaapObject_Status.VALID);
194                 return true;
195
196         }
197         
198         private boolean saveSubs( Feed f ) {
199                 return saveSubs( f, f );
200         }
201         // need to save the Sub objects independently
202         private boolean saveSubs( Feed fnew, Feed req ) {       
203                 ArrayList<DR_Sub> subs = req.getSubs();
204                 if ( subs == null || subs.size() == 0 ) {
205                         logger.info( "No subs specified");
206                 } else {
207                         DR_SubService subSvc = new DR_SubService( fnew.getSubscribeURL() );
208                         ApiError err = new ApiError();
209                         for( int i = 0; i <  subs.size(); i++ ) {
210                                 DR_Sub sub = subs.get(i);
211                                 if ( subSvc.getDr_Sub( sub.getSubId(), err) == null ) {
212                                         subs.set( i,  subSvc.addDr_Sub(sub, err));
213                                         if ( ! err.is2xx())  {
214                                                 logger.error( "i=" + i + " url=" + sub.getDeliveryURL() + " err=" + err.getCode() );
215                                                 return false;
216                                         }
217                                 }
218                                 
219                         }
220                         fnew.setSubs(subs);
221                 }
222
223
224                 fnew.setStatus(DmaapObject_Status.VALID);
225                 return true;
226
227         }
228
229         public  Feed addFeed( Feed req, ApiError err ) {
230
231                 // at least 1 pub is required by DR, so create a default pub if none is specified
232                 if ( req.getPubs().size() == 0 ) {
233                         logger.info( "No pubs specified - creating tmp pub");
234                         ArrayList<DR_Pub> pubs = new ArrayList<DR_Pub>();
235                         pubs.add( new DR_Pub( dcaeLocations.getCentralLocation())
236                                                                 .setRandomUserName()
237                                                                 .setRandomPassword());
238                         req.setPubs(pubs);
239                 } 
240                 
241
242                 DrProvConnection prov = new DrProvConnection();
243                 prov.makeFeedConnection();      
244                 String resp = prov.doPostFeed( req, err );
245                 if ( unit_test.equals( "Yes" ) ) {
246                         // assume resp is null, so need to simulate it
247                         resp = simulateResp( req, "POST" );
248                 }
249                 logger.info( "resp=" + resp );
250                 if ( resp == null ) {
251                         switch( err.getCode() ) {
252                         case 400: 
253                                 err.setFields( "feedName=" + req.getFeedName() + " + feedVersion=" + req.getFeedVersion() );
254                                 break;
255                         case 403:
256                                 err.setCode(500);
257                                 err.setMessage("API deployment/configuration error - contact support");
258                                 err.setFields( "PROV_AUTH_ADDRESSES");
259                                 logger.error( "Prov response: 403. " + err.getMessage() + " regarding " + err.getFields() );
260                                 break;
261                         default:
262                                 err.setCode(500);
263                                 err.setMessage( "Unexpected response from DR backend" );
264                                 err.setFields("response");
265                         }
266                         return null;
267
268                 }
269
270
271                 Feed fnew = new Feed( resp );
272                 logger.info( "fnew status is:" + fnew.getStatus() );
273                 if ( ! fnew.isStatusValid()) {          
274                         err.setCode(500);
275                         err.setMessage( "Unexpected response from DR backend" );
276                         err.setFields("response");              
277                         return null;
278                 }
279                 
280                 //saveChildren( fnew, req );
281                 if ( ! savePubs( fnew, req ) || ! saveSubs( fnew, req ) ) {
282                         err.setCode(Status.BAD_REQUEST.getStatusCode());
283                         err.setMessage("Unable to save Pub or Sub objects");
284                         return null;
285                 }
286                 fnew.setFormatUuid(req.getFormatUuid());
287                 fnew.setLastMod();
288                 feeds.put( fnew.getFeedId(), fnew );
289                 return fnew;
290         }
291                 
292         public Feed updateFeed( Feed req, ApiError err ) {
293         
294                 // at least 1 pub is required by DR, so create a default pub if none is specified
295                 if ( req.getPubs().size() == 0 ) {
296                         logger.info( "No pubs specified - creating tmp pub");
297                         ArrayList<DR_Pub> pubs = new ArrayList<DR_Pub>();
298                         pubs.add( new DR_Pub( dcaeLocations.getCentralLocation())
299                                                                 .setRandomUserName()
300                                                                 .setRandomPassword());
301                         req.setPubs(pubs);
302                 } 
303                 
304                 DrProvConnection prov = new DrProvConnection();
305                 prov.makeFeedConnection( req.getFeedId() );
306                 String resp = prov.doPutFeed( req, err );
307                 if ( unit_test.equals( "Yes" ) ) {
308                         // assume resp is null, so need to simulate it
309                         resp = simulateResp( req, "PUT" );
310                         err.setCode(200);
311                 }
312                 logger.info( "resp=" + resp );
313                 if ( resp == null ) {
314                         switch( err.getCode() ) {
315                         case 400: 
316                                 err.setFields( "feedName=" + req.getFeedName() + " + feedVersion=" + req.getFeedVersion() );
317                                 break;
318                         case 403:
319                                 err.setCode(500);
320                                 err.setMessage("API deployment/configuration error - contact support");
321                                 err.setFields( "PROV_AUTH_ADDRESSES");
322                                 break;
323                         default:
324                                 err.setCode(500);
325                                 err.setMessage( "Unexpected response from DR backend" );
326                                 err.setFields("response");
327                         }
328                         return null;
329                 }
330
331
332                 Feed fnew = new Feed( resp );
333                 logger.info( "fnew status is:" + fnew.getStatus() );
334                 if ( ! fnew.isStatusValid()) {          
335                         err.setCode(500);
336                         err.setMessage( "Unexpected response from DR backend" );
337                         err.setFields("response");              
338                         return null;
339                 }
340
341                 if ( ! savePubs( fnew, req ) || ! saveSubs( fnew, req ) ) {
342                         err.setCode(Status.BAD_REQUEST.getStatusCode());
343                         err.setMessage("Unable to save Pub or Sub objects");
344                         return null;
345                 }
346                 fnew.setFormatUuid(req.getFormatUuid());
347                 fnew.setLastMod();
348                 feeds.put( fnew.getFeedId(), fnew );
349                 return fnew;
350         }
351         
352         
353         //
354         // DR does not actually delete a feed, so we provide two behaviors:
355         // 1) clean up the feed by removing all subs and pubs, mark it here as DELETED.
356         //    then client can add it back if desired.
357         // 2) Call the DR Delete function.  Feed with the same name and version can never be added again
358         //
359         public Feed removeFeed( Feed req, ApiError err ) {
360                 return removeFeed( req, err, true );
361         }
362         
363         public Feed removeFeed( Feed req, ApiError err, boolean hitDR ) {
364                 
365                 // strip pubs and subs from feed first no matter what
366                 ArrayList<DR_Pub> pubs = pubService.getDr_PubsByFeedId( req.getFeedId() );
367                 for( DR_Pub pub: pubs ) {
368                         pubService.removeDr_Pub(pub.getPubId(), err, hitDR);
369                         if ( ! err.is2xx()) {
370                                 return req;
371                         }
372                 }
373                 ArrayList<DR_Sub> subs = subService.getDr_SubsByFeedId( req.getFeedId() );
374                 for ( DR_Sub sub: subs ) {
375                         subService.removeDr_Sub(sub.getSubId(), err, hitDR);
376                         if ( ! err.is2xx()) {
377                                 return req;
378                         }
379                 }
380                 
381                 if ( ! hitDR ) {
382                         return feeds.remove(req.getFeedId());   
383                 }
384         
385                 if ( deleteHandling.equalsIgnoreCase("DeleteOnDR")) {
386                         DrProvConnection prov = new DrProvConnection();
387                         prov.makeFeedConnection( req.getFeedId() );
388                         String resp = prov.doDeleteFeed( req, err );
389                         if ( unit_test.equals( "Yes" ) ) {
390                                 // assume resp is null, so need to simulate it
391                                 resp = simulateDelResp( req );
392                         }
393                         logger.info( "resp=" + resp );
394                         if ( resp == null ) {
395                                 switch( err.getCode() ) {
396                                 case 400: 
397                                         err.setFields( "feedName=" + req.getFeedName() + " + feedVersion=" + req.getFeedVersion() );
398                                         break;
399                                 case 403:
400                                         err.setCode(500);
401                                         err.setMessage("API deployment/configuration error - contact support");
402                                         err.setFields( "PROV_AUTH_ADDRESSES");
403                                         break;
404                                 default:
405                                         err.setCode(500);
406                                         err.setMessage( "Unexpected response from DR backend" );
407                                         err.setFields("response");
408                                 }
409                                 return req;  // return back the requested feed - implies it wasn't removed
410                         }
411                         return feeds.remove(req.getFeedId());
412                 } else {
413                 
414                         logger.info( "Disable pubs for deleted feed - creating tmp pub");
415                         ArrayList<DR_Pub> tmppub = new ArrayList<DR_Pub>();
416                         tmppub.add( new DR_Pub( dcaeLocations.getCentralLocation())
417                                                                 .setRandomUserName()
418                                                                 .setRandomPassword());
419                         req.setPubs(tmppub);
420                         req.setSubs(null);
421                         Feed fnew = updateFeed( req, err );
422                         if ( ! err.is2xx()) {
423                                 return req;
424                         }
425                         fnew.setStatus(DmaapObject_Status.DELETED);
426                         feeds.put( fnew.getFeedId(), fnew );
427                         return null;    
428                 }
429
430                 
431         }       
432         
433         
434         /*
435          * sync will retrieve current config from DR and add it to the DB
436          * when hard = true, then first git rid of current DR provisioning data (from the DB)
437          */
438         public void sync( boolean hard, ApiError err ) {
439         
440                 if ( hard ) {
441                         
442                         ArrayList<Feed> flist = new ArrayList<Feed>(this.getAllFeeds());
443                         for ( Iterator<Feed> it = flist.iterator(); it.hasNext(); ) {
444                                 Feed f = it.next();
445         
446                                 @SuppressWarnings("unused")
447                                 Feed old = removeFeed( f, err, false );
448                                 if (! err.is2xx()) {
449                                         return;
450                                 }
451                         }
452                 }
453                 
454                 DrProvConnection prov = new DrProvConnection();
455                 prov.makeDumpConnection();
456                 String resp = prov.doGetDump( err );
457                 if (! err.is2xx()) {
458                         return;
459                 }
460                 logger.debug("sync: resp from DR is: " + resp);
461                 
462                 JSONParser parser = new JSONParser();
463                 JSONObject jsonObj;
464                 try {
465                         jsonObj = (JSONObject) parser.parse( resp );
466                 } catch ( ParseException pe ) {
467                         logger.error( "Error parsing provisioning data: " + resp );
468                         err.setCode(500);
469                         return;
470                 }
471                 
472                 int i;
473
474                 JSONArray feedsArray = (JSONArray) jsonObj.get( "feeds");
475                 for( i = 0; i < feedsArray.size(); i++ ) {
476                         JSONObject entry = (JSONObject) feedsArray.get(i);
477                         Feed fnew = new Feed( entry.toJSONString() );
478                         
479                         logger.info( "fnew status is:" + fnew.getStatus() );
480                         if ( ! fnew.isStatusValid()) {          
481                                 err.setCode(500);
482                                 err.setMessage( "Unexpected response from DR backend" );
483                                 err.setFields("response");              
484                                 return;
485                         }
486                         
487                                 if ( ! savePubs( fnew )  ) {
488                                 err.setCode(Status.BAD_REQUEST.getStatusCode());
489                                 err.setMessage("Unable to save Pub or Sub objects");
490                                 return; 
491                         }
492                         fnew.setFormatUuid(fnew.getFormatUuid());
493                         fnew.setLastMod();
494                         feeds.put( fnew.getFeedId(), fnew );
495
496                 }
497                 
498                 JSONArray subArray = (JSONArray) jsonObj.get( "subscriptions");
499                 for( i = 0; i < subArray.size(); i++ ) {
500                         JSONObject entry = (JSONObject) subArray.get(i);
501                         DR_Sub snew = new DR_Sub( entry.toJSONString() );
502                         
503                         logger.info( "snew status is:" + snew.getStatus() );
504                         if ( ! snew.isStatusValid()) {          
505                                 err.setCode(500);
506                                 err.setMessage( "Unexpected response from DR backend" );
507                                 err.setFields("response");              
508                                 return;
509                         }
510                         
511                         dr_subs.put( snew.getSubId(), snew );
512
513                 }
514                 err.setCode(200);
515                 return;
516                 
517         }
518
519         private String simulateResp( Feed f, String action ){
520                 String server = "localhost";
521                 String feedid;
522                 if ( action.equals( "POST" ) ) { 
523                         RandomInteger ran = new RandomInteger(10000);
524                         feedid = Integer.toString( ran.next() );
525                 } else if ( action.equals( "PUT" ) ) {
526                         feedid = f.getFeedId();
527                 } else {
528                         feedid = "99";
529                 }
530                 String ret = String.format( 
531 "{\"suspend\":false,\"groupid\":0,\"description\":\"%s\",\"version\":\"1.0\",\"authorization\":",
532                         f.getFeedDescription() );
533
534                 String endpoints = "{\"endpoint_addrs\":[],\"classification\":\"unclassified\",\"endpoint_ids\":[";
535                 String sep = "";
536                 for( DR_Pub pub: f.getPubs()) {
537                         endpoints +=  String.format( "%s{\"password\":\"%s\",\"id\":\"%s\"}", 
538                                         sep, pub.getUserpwd(), pub.getUsername() );
539                         sep = ",";
540                         
541                 }
542                 endpoints += "]},";
543                 ret += endpoints;
544                 
545                 ret += String.format(
546                 "\"name\":\"%s\",\"business_description\":\"\",\"publisher\":\"sim\",\"links\":{\"subscribe\":\"https://%s/subscribe/%s\",\"log\":\"https://%s/feedlog/%s\",\"publish\":\"https://%s/publish/%s\",\"self\":\"https://%s/feed/%s\"}}",
547
548                         f.getFeedName(),
549                         server, feedid,
550                         server, feedid,
551                         server, feedid,
552                         server, feedid
553                                 );
554                 logger.info( "simulateResp ret=" + ret );
555                 return ret;
556         }
557         private String simulateDelResp( Feed f ){
558                 String server = "localhost";
559                 String feedid = f.getFeedId();
560                 String ret = String.format( 
561 "{\"suspend\":true,\"groupid\":0,\"description\":\"%s\",\"version\":\"1.0\",\"authorization\":{\"endpoint_addrs\":[],\"classification\":\"unclassified\",\"endpoint_ids\":[{\"password\":\"topSecret123\",\"id\":\"sim\"}]},\"name\":\"%s\",\"business_description\":\"\",\"publisher\":\"sim\",\"links\":{\"subscribe\":\"https://%s/subscribe/%s\",\"log\":\"https://%s/feedlog/%s\",\"publish\":\"https://%s/publish/%s\",\"self\":\"https://%s/feed/%s\"}}",
562                 f.getFeedDescription(),
563                 f.getFeedName(),
564                 server, feedid,
565                 server, feedid,
566                 server, feedid,
567                 server, feedid
568
569                 );
570                 return ret;
571         }
572 }