From 81352e8c9570dc8e442ca61edfb991d34b62eb08 Mon Sep 17 00:00:00 2001 From: dglFromAtt Date: Fri, 29 Mar 2019 16:46:43 -0400 Subject: [PATCH] Added new feeds/sync API Change-Id: I86576cdd5be67ca80089d092458782429253e17b Signed-off-by: dglFromAtt Issue-ID: DMAAP-1021 Signed-off-by: dglFromAtt --- .../onap/dmaap/dbcapi/client/DrProvConnection.java | 151 +++++++++++++++++++++ .../onap/dmaap/dbcapi/resources/FeedResource.java | 32 +++++ .../onap/dmaap/dbcapi/service/DR_PubService.java | 6 +- .../onap/dmaap/dbcapi/service/DR_SubService.java | 19 ++- .../org/onap/dmaap/dbcapi/service/FeedService.java | 109 ++++++++++++++- .../onap/dmaap/dbcapi/service/FeedServiceTest.java | 9 ++ 6 files changed, 318 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/onap/dmaap/dbcapi/client/DrProvConnection.java b/src/main/java/org/onap/dmaap/dbcapi/client/DrProvConnection.java index d603679..54fa84e 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/client/DrProvConnection.java +++ b/src/main/java/org/onap/dmaap/dbcapi/client/DrProvConnection.java @@ -49,6 +49,7 @@ public class DrProvConnection extends BaseLoggingClass { private String feedContentType; private String subContentType; private String unit_test; + private String provURI; private HttpsURLConnection uc; @@ -63,6 +64,7 @@ public class DrProvConnection extends BaseLoggingClass { behalfHeader = p.getProperty( "DR.onBehalfHeader", "X-DMAAP-DR-ON-BEHALF-OF"); feedContentType = p.getProperty( "DR.feedContentType", "application/vnd.dmaap-dr.feed"); subContentType = p.getProperty( "DR.subContentType", "application/vnd.dmaap-dr.subscription"); + provURI = p.getProperty( "DR.ProvisioningURI", "/internal/prov"); logger.info( "provURL=" + provURL + " provApi=" + provApi + " behalfHeader=" + behalfHeader + " feedContentType=" + feedContentType + " subContentType=" + subContentType ); unit_test = p.getProperty( "UnitTest", "No" ); @@ -97,6 +99,10 @@ public class DrProvConnection extends BaseLoggingClass { sub, nodep ); return makeConnection( provURL + uri ); } + public boolean makeDumpConnection() { + String url = provURL + provURI; + return makeConnection( url ); + } public boolean makeNodesConnection( String varName ) { String uri = String.format("/internal/api/%s", varName); @@ -923,5 +929,150 @@ public class DrProvConnection extends BaseLoggingClass { } + // add double-quotes around a value + // hope his is easier to read than in-line escaping... + private String dq( String v ) { + return ( "\"" + v + "\""); + } + private String dq( String k, String v) { + return( dq(k) + ":" + dq(v)); + } + private String dqc( String k, String v) { + return( dq(k) + ":" + dq(v) + ","); + } + + private String dumpSimulation() { + logger.info( "enter dumpSimulation()"); + String responseBody = + "{" + + dq("feeds") + ":[" + + "{" + dq( "suspend") + ":false," + + dq( "groupid") + ":0," + + dqc( "description", "Some description" ) + + dqc( "version", "m1.1") + + dq( "authorization") + ":" + + "{" + dq( "endpoint_addrs" ) + ":[]," + + dq( "classification", "unclassified") + + dq( "endpoint_ids") + ":[{" + + dqc( "password", "dradmin" ) + + dq( "id", "dradmin") + + "}]}" + + dq( "last_mod") + ":1553738110000," + + dq( "deleted") + ":false," + + dq( "feedid") + ":1," + + dqc( "name", "Default PM Feed") + + dq( "business_description") + ":\"\"," + + dqc( "publisher", "onap") + + dq( "links") + ":{" + + dqc( "subscribe", "https://dmaap-dr-prov/subscribe/1") + + dqc( "log", "https://dmaap-dr-prov/feedlog/1") + + dqc( "publish", "https://dmaap-dr-prov/publish/1") + + dq( "self", "https:/dmaap-dr-prov/feed/1") + + "}" + + dq( "created_date") + ":1553738110000 }" + + "]," + + dq( "groups") + ":[" + + "]," + + dq( "subscriptions") + ":[" + + "]," + + dq( "ingress") + ":[" + + "]," + + dq( "egress") + ":{" + + "}," + + dq( "routing") + ":[" + + "]," + + "}"; + return responseBody; + } + + public String doGetDump( ApiError err ) { + logger.info( "entry: doGetDump() " ); + + String responsemessage = null; + String responseBody = null; + + try { + + uc.setRequestMethod("GET"); + int rc = -1; + + + try { + uc.connect(); + + + } catch (ProtocolException pe) { + + // Rcvd error instead of 100-Continue + try { + // work around glitch in Java 1.7.0.21 and likely others + // without this, Java will connect multiple times to the server to run the same request + uc.setDoOutput(false); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } + + rc = uc.getResponseCode(); + logger.info( "http response code:" + rc ); + responsemessage = uc.getResponseMessage(); + logger.info( "responsemessage=" + responsemessage ); + + + + if (responsemessage == null) { + + // work around for glitch in Java 1.7.0.21 and likely others + // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is + String h0 = uc.getHeaderField(0); + if (h0 != null) { + int i = h0.indexOf(' '); + int j = h0.indexOf(' ', i + 1); + if (i != -1 && j != -1) { + responsemessage = h0.substring(j + 1); + } + } + } + + err.setCode(rc); // may not really be an error, but we save rc + if (rc == 200 ) { + responseBody = bodyToString( uc.getInputStream() ); + logger.info( "responseBody=" + responseBody ); + } else { + err.setMessage(responsemessage); + } + + + } catch (ConnectException ce) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doGetNodes because unit_test =" + unit_test ); + responseBody = dumpSimulation(); + + } else { + errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage() ); + err.setCode( 500 ); + err.setMessage("Backend connection refused"); + logger.error(ce.getMessage(), ce); + } + } catch (Exception e) { + if ( unit_test.equals( "Yes" ) ) { + err.setCode(200); + err.setMessage( "simulated response"); + logger.info( "artificial 200 response from doGetNodes because unit_test =" + unit_test ); + responseBody = dumpSimulation(); + + } else { + logger.error("Unable to read response ", e.getMessage()); + } + } finally { + + if ( uc != null ) uc.disconnect(); + } + + return responseBody; + + } } diff --git a/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java b/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java index 86a79f2..6df135d 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java +++ b/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java @@ -232,4 +232,36 @@ public class FeedResource extends BaseLoggingClass { } return responseBuilder.success(nfeed); } + + @PUT + @ApiOperation( value = "sync feeds to existing DR", + notes = "When Bus Controller is deployed after DR, then it is possible" + + "that DR has previous provisioning data that needs to be imported" + + "into Bus Controller.", + response = Feed.class ) + @ApiResponses( value = { + @ApiResponse( code = 200, message = "Success", response = Feed.class), + @ApiResponse( code = 400, message = "Error", response = ApiError.class ) + }) + @Path( "/sync") + public Response syncFeeds ( + @QueryParam("hard") String hardParam + ) { + ApiService resp = new ApiService(); + + FeedService feedService = new FeedService(); + boolean hard = false; + if ( hardParam != null && hardParam.equalsIgnoreCase("true")) { + hard = true; + } + feedService.sync( hard, resp.getErr() ); + if ( resp.getErr().is2xx()) { + List nfeeds = feedService.getAllFeeds(); + GenericEntity> list = new GenericEntity>(nfeeds) { + }; + return responseBuilder.success(list); + } + return responseBuilder.error(resp.getErr()); + } + } diff --git a/src/main/java/org/onap/dmaap/dbcapi/service/DR_PubService.java b/src/main/java/org/onap/dmaap/dbcapi/service/DR_PubService.java index 13717da..eb5b514 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/service/DR_PubService.java +++ b/src/main/java/org/onap/dmaap/dbcapi/service/DR_PubService.java @@ -129,7 +129,11 @@ public class DR_PubService extends BaseLoggingClass{ } public DR_Pub removeDr_Pub( String pubId, ApiError err ) { - + return removeDr_Pub( pubId, err, true ); + } + + + public DR_Pub removeDr_Pub( String pubId, ApiError err, boolean hitDR ) { DR_Pub pub = dr_pubs.get( pubId ); if ( pub == null ) { err.setCode(Status.NOT_FOUND.getStatusCode()); diff --git a/src/main/java/org/onap/dmaap/dbcapi/service/DR_SubService.java b/src/main/java/org/onap/dmaap/dbcapi/service/DR_SubService.java index c14b1fd..0a583a0 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/service/DR_SubService.java +++ b/src/main/java/org/onap/dmaap/dbcapi/service/DR_SubService.java @@ -173,6 +173,11 @@ public class DR_SubService extends BaseLoggingClass { } public void removeDr_Sub( String key, ApiError apiError ) { + removeDr_Sub( key, apiError, true ); + return; + } + + public void removeDr_Sub( String key, ApiError apiError, boolean hitDR ) { logger.debug( "enter removeDR_Subs()"); DR_Sub sub = dr_subs.get( key ); @@ -180,11 +185,15 @@ public class DR_SubService extends BaseLoggingClass { apiError.setCode(Status.NOT_FOUND.getStatusCode()); apiError.setFields( "subId"); apiError.setMessage("subId " + key + " not found"); - } else { - DrProvConnection prov = new DrProvConnection(); - prov.makeSubPutConnection( key ); - String resp = prov.doDeleteDr_Sub( sub, apiError ); - logger.debug( "resp=" + resp ); + } else { + if ( hitDR ) { + DrProvConnection prov = new DrProvConnection(); + prov.makeSubPutConnection( key ); + String resp = prov.doDeleteDr_Sub( sub, apiError ); + logger.debug( "resp=" + resp ); + } else { + apiError.setCode(200); + } if ( apiError.is2xx() || unit_test.equals( "Yes" ) ) { dr_subs.remove(key); diff --git a/src/main/java/org/onap/dmaap/dbcapi/service/FeedService.java b/src/main/java/org/onap/dmaap/dbcapi/service/FeedService.java index de18d95..77d11ab 100644 --- a/src/main/java/org/onap/dmaap/dbcapi/service/FeedService.java +++ b/src/main/java/org/onap/dmaap/dbcapi/service/FeedService.java @@ -25,12 +25,17 @@ package org.onap.dmaap.dbcapi.service; import org.onap.dmaap.dbcapi.util.RandomInteger; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Map; import javax.ws.rs.core.Response.Status; import org.apache.log4j.Logger; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; import org.onap.dmaap.dbcapi.client.DrProvConnection; import org.onap.dmaap.dbcapi.database.DatabaseClass; import org.onap.dmaap.dbcapi.logging.BaseLoggingClass; @@ -38,12 +43,14 @@ import org.onap.dmaap.dbcapi.model.ApiError; import org.onap.dmaap.dbcapi.model.DR_Pub; import org.onap.dmaap.dbcapi.model.DR_Sub; import org.onap.dmaap.dbcapi.model.Feed; +import org.onap.dmaap.dbcapi.model.MR_Client; import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status; import org.onap.dmaap.dbcapi.util.DmaapConfig; public class FeedService extends BaseLoggingClass { private Map feeds = DatabaseClass.getFeeds(); + private Map dr_subs = DatabaseClass.getDr_subs(); private DR_PubService pubService = new DR_PubService(); private DR_SubService subService = new DR_SubService(); private DcaeLocationService dcaeLocations = new DcaeLocationService(); @@ -68,6 +75,10 @@ public class FeedService extends BaseLoggingClass { ArrayList subs = subService.getDr_SubsByFeedId( f.getFeedId() ); f.setSubs(subs); } + + public List getAllFeeds(){ + return getAllFeeds(null, null, null); + } public List getAllFeeds( String name, String ver, String match ) { logger.info( "getAllFeeds: name=" + name + " ver=" + ver + " match=" + match); @@ -351,22 +362,30 @@ public class FeedService extends BaseLoggingClass { // 2) Call the DR Delete function. Feed with the same name and version can never be added again // public Feed removeFeed( Feed req, ApiError err ) { + return removeFeed( req, err, true ); + } + + public Feed removeFeed( Feed req, ApiError err, boolean hitDR ) { // strip pubs and subs from feed first no matter what ArrayList pubs = pubService.getDr_PubsByFeedId( req.getFeedId() ); for( DR_Pub pub: pubs ) { - pubService.removeDr_Pub(pub.getPubId(), err); + pubService.removeDr_Pub(pub.getPubId(), err, hitDR); if ( ! err.is2xx()) { return req; } } ArrayList subs = subService.getDr_SubsByFeedId( req.getFeedId() ); for ( DR_Sub sub: subs ) { - subService.removeDr_Sub(sub.getSubId(), err); + subService.removeDr_Sub(sub.getSubId(), err, hitDR); if ( ! err.is2xx()) { return req; } } + + if ( ! hitDR ) { + return feeds.remove(req.getFeedId()); + } if ( deleteHandling.equalsIgnoreCase("DeleteOnDR")) { DrProvConnection prov = new DrProvConnection(); @@ -415,6 +434,92 @@ public class FeedService extends BaseLoggingClass { } + + + /* + * sync will retrieve current config from DR and add it to the DB + * when hard = true, then first git rid of current DR provisioning data (from the DB) + */ + public void sync( boolean hard, ApiError err ) { + + if ( hard ) { + + ArrayList flist = new ArrayList(this.getAllFeeds()); + for ( Iterator it = flist.iterator(); it.hasNext(); ) { + Feed f = it.next(); + + @SuppressWarnings("unused") + Feed old = removeFeed( f, err, false ); + if (! err.is2xx()) { + return; + } + } + } + + DrProvConnection prov = new DrProvConnection(); + prov.makeDumpConnection(); + String resp = prov.doGetDump( err ); + if (! err.is2xx()) { + return; + } + logger.debug("sync: resp from DR is: " + resp); + + JSONParser parser = new JSONParser(); + JSONObject jsonObj; + try { + jsonObj = (JSONObject) parser.parse( resp ); + } catch ( ParseException pe ) { + logger.error( "Error parsing provisioning data: " + resp ); + err.setCode(500); + return; + } + + int i; + + JSONArray feedsArray = (JSONArray) jsonObj.get( "feeds"); + for( i = 0; i < feedsArray.size(); i++ ) { + JSONObject entry = (JSONObject) feedsArray.get(i); + Feed fnew = new Feed( entry.toJSONString() ); + + logger.info( "fnew status is:" + fnew.getStatus() ); + if ( ! fnew.isStatusValid()) { + err.setCode(500); + err.setMessage( "Unexpected response from DR backend" ); + err.setFields("response"); + return; + } + + if ( ! savePubs( fnew ) ) { + err.setCode(Status.BAD_REQUEST.getStatusCode()); + err.setMessage("Unable to save Pub or Sub objects"); + return; + } + fnew.setFormatUuid(fnew.getFormatUuid()); + fnew.setLastMod(); + feeds.put( fnew.getFeedId(), fnew ); + + } + + JSONArray subArray = (JSONArray) jsonObj.get( "subscriptions"); + for( i = 0; i < subArray.size(); i++ ) { + JSONObject entry = (JSONObject) subArray.get(i); + DR_Sub snew = new DR_Sub( entry.toJSONString() ); + + logger.info( "snew status is:" + snew.getStatus() ); + if ( ! snew.isStatusValid()) { + err.setCode(500); + err.setMessage( "Unexpected response from DR backend" ); + err.setFields("response"); + return; + } + + dr_subs.put( snew.getSubId(), snew ); + + } + err.setCode(200); + return; + + } private String simulateResp( Feed f, String action ){ String server = "localhost"; diff --git a/src/test/java/org/onap/dmaap/dbcapi/service/FeedServiceTest.java b/src/test/java/org/onap/dmaap/dbcapi/service/FeedServiceTest.java index d601bc4..acbc738 100644 --- a/src/test/java/org/onap/dmaap/dbcapi/service/FeedServiceTest.java +++ b/src/test/java/org/onap/dmaap/dbcapi/service/FeedServiceTest.java @@ -95,5 +95,14 @@ public class FeedServiceTest { } + + @Test + public void syncTestHard() { + ApiError err = new ApiError(); + ds.sync( true, err ); + + assert( 200 == err.getCode()); + } + } -- 2.16.6