Merge "Fixed bug in BridgeResource.java"
authorvarun gudisena <varuneshwar.gudisena@att.com>
Fri, 18 Jan 2019 14:40:57 +0000 (14:40 +0000)
committerGerrit Code Review <gerrit@onap.org>
Fri, 18 Jan 2019 14:40:57 +0000 (14:40 +0000)
29 files changed:
INFO.yaml [new file with mode: 0644]
docs/api.rst
pom.xml
src/main/java/org/onap/dmaap/dbcapi/aaf/AafLurService.java
src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java [new file with mode: 0644]
src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java [new file with mode: 0644]
src/main/java/org/onap/dmaap/dbcapi/aaf/AafService.java
src/main/java/org/onap/dmaap/dbcapi/aaf/AafUserRole.java [new file with mode: 0644]
src/main/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFish.java
src/main/java/org/onap/dmaap/dbcapi/client/MrProvConnection.java
src/main/java/org/onap/dmaap/dbcapi/database/DatabaseClass.java
src/main/java/org/onap/dmaap/dbcapi/model/DmaapObject.java
src/main/java/org/onap/dmaap/dbcapi/model/MR_Client.java
src/main/java/org/onap/dmaap/dbcapi/model/Topic.java
src/main/java/org/onap/dmaap/dbcapi/resources/BridgeResource.java
src/main/java/org/onap/dmaap/dbcapi/resources/DR_SubResource.java
src/main/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResource.java
src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java
src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClientResource.java
src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResource.java
src/main/java/org/onap/dmaap/dbcapi/resources/TopicResource.java
src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java
src/main/java/org/onap/dmaap/dbcapi/service/MR_ClientService.java
src/main/java/org/onap/dmaap/dbcapi/service/TopicService.java
src/main/resources/schema_10.sql [new file with mode: 0644]
src/main/resources/schema_11.sql [new file with mode: 0644]
src/test/java/org/onap/dmaap/dbcapi/resources/DR_SubResourceTest.java
src/test/java/org/onap/dmaap/dbcapi/testframework/ReflectionHarness.java
version.properties

diff --git a/INFO.yaml b/INFO.yaml
new file mode 100644 (file)
index 0000000..172026d
--- /dev/null
+++ b/INFO.yaml
@@ -0,0 +1,42 @@
+---
+project: 'dmaap-dbcapi'
+project_creation_date: '2017-08-29'
+lifecycle_state: 'Incubation'
+project_lead: &onap_releng_ptl
+    name: 'Ram Koya'
+    email: 'rk541m@att.com'
+    id: 'rampi_k'
+    company: 'ATT'
+    timezone: 'America/Dallas'
+primary_contact: *onap_releng_ptl
+issue_tracking:
+    type: 'jira'
+    url: 'https://jira.onap.org/projects/DMAAP'
+    key: 'DMAAP'
+meetings:
+    - type: 'zoom'
+        agenda: ''
+        url: 'https://wiki.onap.org/pages/viewpage.action?pageId=13599275'
+        server: 'n/a'
+        channel: 'n/a'
+        repeats: 'weekly'
+        time: '13:00 UTC'
+committers:
+    - <<: *onap_releng_ptl
+    - name: 'Ramdas Sawant'
+        email: 'rs873m@att.com'
+        company: 'ATT'
+        id: 'rs873m'
+        timezone: 'America/Dallas'
+    - name: 'Varun Gudisena'
+        email: 'vg411h@att.com'
+        company: 'ATT'
+        id: 'vg411h'
+        timezone: 'America/Dallas'
+tsc:
+    approval: 'https://lists.onap.org/pipermail/onap-tsc'
+    changes:
+        - type: 'Removal'
+          name: 'Habib Madani'
+          name: 'Xinhui Li'
+          name: 'Jing Wang'
index 90595fb..9eab4da 100644 (file)
@@ -8,17 +8,39 @@ DMaaP Bus Controller REST API 1.1.0
 Description
 ~~~~~~~~~~~
 
-provides an API for OpenDCAE components which need to provision underlying DMaaP technologies (Data Router and Message Router). Primary clients for this API are anticipated to be the OpenDCAE Controller, OpenDCAE Orchestrator, OpenDCAE Inventory and the ECOMP Portal.
-
-Objects managed by DMaaP are deployed in a dcaeLocation which is a unique identifier for an OpenStack tenant for a dcaeLayer, opendcae-central (aka ecomp) or opendcae-local-ntc (aka edge).
-
-A dcaeEnvironment (e.g. FTL or prod) has a single DMaaP. A DMaaP is managed by a one or more stateless DMaaP Bus Controller(s), though Bus Controller relies on PGaaS for persistence. Each DMaaP has a single instance of Data Router, which has 1 or more DR_Nodes deployed at each dcaeLocation. DR Clients of type DR_Pub generally publish to a DR_Node that is local to its dcaeLocation. Routing for a Feed is determined by the dcaelocation of its DR_Sub clients.
-
-A DMaaP may have many Message Router instances. Each instance is deployed as an MR_Cluster. One MR_Cluster is deployed at each dcaeLocation. MR_Clients generally communicate to the MR_Cluster at the same dcaeLocation. Replication of messages between MR_Clusters is accomplished by MR Bridge, which is provioned by DMaaP Bus Controller based on Topic attributes.
-
-Therefore, the role of DMaaP Bus Controller is to support other DCAE infrastructure components to dynamically provision DMaaP services on behalf of DMaaP clients, and to assist in any management or discovery activity of its clients.
-
-A convention of this API is to return JSON responses per OpenStack style.
+provides an API for OpenDCAE components which need to provision
+                                                                       underlying DMaaP technologies (Data Router and Message Router).
+                                                                       Primary clients for this API are anticipated to be the OpenDCAE
+                                                                       Controller, OpenDCAE Orchestrator, OpenDCAE Inventory and the
+                                                                       ECOMP Portal.
+
+                                                                       Objects managed by DMaaP are deployed in a dcaeLocation which is a
+                                                                       unique identifier for an OpenStack tenant for a dcaeLayer,
+                                                                       opendcae-central (aka ecomp) or opendcae-local-ntc (aka edge).
+
+                                                                       A dcaeEnvironment (e.g. FTL or prod) has a single DMaaP. A
+                                                                       DMaaP is managed by a one or more stateless DMaaP Bus
+                                                                       Controller(s), though Bus Controller relies on PGaaS for
+                                                                       persistence. Each DMaaP has a single instance of Data Router,
+                                                                       which has 1 or more DR_Nodes deployed at each dcaeLocation. DR
+                                                                       Clients of type DR_Pub generally publish to a DR_Node that is
+                                                                       local to its dcaeLocation. Routing for a Feed is determined by
+                                                                       the dcaelocation of its DR_Sub clients.
+
+                                                                       A DMaaP may have many Message Router instances. Each instance is
+                                                                       deployed as an MR_Cluster. One MR_Cluster is deployed at each
+                                                                       dcaeLocation. MR_Clients generally communicate to the
+                                                                       MR_Cluster at the same dcaeLocation. Replication of messages
+                                                                       between MR_Clusters is accomplished by MR Bridge, which is
+                                                                       provioned by DMaaP Bus Controller based on Topic attributes.
+
+                                                                       Therefore, the role of DMaaP Bus Controller is to support other
+                                                                       DCAE infrastructure components to dynamically provision DMaaP
+                                                                       services on behalf of DMaaP clients, and to assist in any
+                                                                       management or discovery activity of its clients.
+
+                                                                       A convention of this API is to return JSON responses per
+                                                                       OpenStack style.
 
 
 
@@ -72,7 +94,7 @@ Description
 
 .. raw:: html
 
-    Returns array of  `BrTopic` objects. If source and target query params are specified, only report on that bridge.  If detail param is true, list topics names, else just a count is returned
+    Returns array of  `BrTopic` objects. If source and target query params are specified, only report on that bridge.  If detail param is true, list topics names, else just a count is returned.
 
 Parameters
 ++++++++++
@@ -82,8 +104,7 @@ Parameters
     :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
     :widths: 20, 15, 10, 10, 10, 20, 30
 
-        source | query | No | string |  |  | 
-        target | query | No | string |  |  | 
+        mmagent | query | No | string |  |  | 
         detail | query | No | boolean |  |  | 
 
 
@@ -100,24 +121,17 @@ Responses
 Success
 
 
-Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+Type: :ref:`BrTopic <d_d71baea9d8e4e59bc395ef51f45dff1b>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "accessKeyOwner": "somestring", 
-        "bridgeAdminTopic": "somestring", 
-        "dmaapName": "somestring", 
-        "drProvUrl": "somestring", 
-        "lastMod": "2015-01-01T15:00:00.000Z", 
-        "loggingUrl": "somestring", 
-        "nodeKey": "somestring", 
-        "status": "EMPTY", 
-        "statusValid": true, 
-        "topicNsRoot": "somestring", 
-        "version": "somestring"
+        "brSource": "somestring", 
+        "brTarget": "somestring", 
+        "mmAgentName": "somestring", 
+        "topicCount": 1
     }
 
 **400**
@@ -157,7 +171,7 @@ Description
 
 .. raw:: html
 
-    replace the topic list for a specific Bridge.  Use JSON Body for value to replace whitelist, but if refreshFlag param is true, simply refresh using existing whitelist
+    replace the topic list for a specific Bridge.  Use JSON Body for value to replace whitelist, but if refreshFlag param is true, simply refresh using existing whitelist.If split param is true, spread whitelist over smaller mmagents.
 
 Parameters
 ++++++++++
@@ -167,9 +181,9 @@ Parameters
     :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
     :widths: 20, 15, 10, 10, 10, 20, 30
 
-        source | query | No | string |  |  | 
-        target | query | No | string |  |  | 
+        mmagent | query | No | string |  |  | 
         refresh | query | No | boolean |  |  | 
+        split | query | No | boolean |  |  | 
 
 
 Request
@@ -185,24 +199,17 @@ Responses
 Success
 
 
-Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+Type: :ref:`BrTopic <d_d71baea9d8e4e59bc395ef51f45dff1b>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "accessKeyOwner": "somestring", 
-        "bridgeAdminTopic": "somestring", 
-        "dmaapName": "somestring", 
-        "drProvUrl": "somestring", 
-        "lastMod": "2015-01-01T15:00:00.000Z", 
-        "loggingUrl": "somestring", 
-        "nodeKey": "somestring", 
-        "status": "EMPTY", 
-        "statusValid": true, 
-        "topicNsRoot": "somestring", 
-        "version": "somestring"
+        "brSource": "somestring", 
+        "brTarget": "somestring", 
+        "mmAgentName": "somestring", 
+        "topicCount": 1
     }
 
 **400**
@@ -267,24 +274,22 @@ Responses
 Success
 
 
-Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+Type: :ref:`DcaeLocation <d_47d80e451933beb623fcf5257867cbcb>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "accessKeyOwner": "somestring"
-        "bridgeAdminTopic": "somestring", 
-        "dmaapName": "somestring", 
-        "drProvUrl": "somestring", 
+        "central": true
+        "clli": "somestring", 
+        "dcaeLayer": "somestring", 
+        "dcaeLocationName": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "loggingUrl": "somestring"
-        "nodeKey": "somestring", 
+        "local": true
+        "openStackAvailabilityZone": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "topicNsRoot": "somestring", 
-        "version": "somestring"
+        "subnet": "somestring"
     }
 
 **400**
@@ -350,24 +355,22 @@ Responses
 successful operation
 
 
-Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+Type: :ref:`DcaeLocation <d_47d80e451933beb623fcf5257867cbcb>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "accessKeyOwner": "somestring"
-        "bridgeAdminTopic": "somestring", 
-        "dmaapName": "somestring", 
-        "drProvUrl": "somestring", 
+        "central": true
+        "clli": "somestring", 
+        "dcaeLayer": "somestring", 
+        "dcaeLocationName": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "loggingUrl": "somestring"
-        "nodeKey": "somestring", 
+        "local": true
+        "openStackAvailabilityZone": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "topicNsRoot": "somestring", 
-        "version": "somestring"
+        "subnet": "somestring"
     }
 
 **204**
@@ -376,24 +379,22 @@ Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
 Success
 
 
-Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+Type: :ref:`DcaeLocation <d_47d80e451933beb623fcf5257867cbcb>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "accessKeyOwner": "somestring"
-        "bridgeAdminTopic": "somestring", 
-        "dmaapName": "somestring", 
-        "drProvUrl": "somestring", 
+        "central": true
+        "clli": "somestring", 
+        "dcaeLayer": "somestring", 
+        "dcaeLocationName": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "loggingUrl": "somestring"
-        "nodeKey": "somestring", 
+        "local": true
+        "openStackAvailabilityZone": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "topicNsRoot": "somestring", 
-        "version": "somestring"
+        "subnet": "somestring"
     }
 
 **400**
@@ -459,24 +460,22 @@ Responses
 Success
 
 
-Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+Type: :ref:`DcaeLocation <d_47d80e451933beb623fcf5257867cbcb>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "accessKeyOwner": "somestring"
-        "bridgeAdminTopic": "somestring", 
-        "dmaapName": "somestring", 
-        "drProvUrl": "somestring", 
+        "central": true
+        "clli": "somestring", 
+        "dcaeLayer": "somestring", 
+        "dcaeLocationName": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "loggingUrl": "somestring"
-        "nodeKey": "somestring", 
+        "local": true
+        "openStackAvailabilityZone": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "topicNsRoot": "somestring", 
-        "version": "somestring"
+        "subnet": "somestring"
     }
 
 **400**
@@ -532,24 +531,22 @@ Responses
 Success
 
 
-Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+Type: :ref:`DcaeLocation <d_47d80e451933beb623fcf5257867cbcb>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "accessKeyOwner": "somestring"
-        "bridgeAdminTopic": "somestring", 
-        "dmaapName": "somestring", 
-        "drProvUrl": "somestring", 
+        "central": true
+        "clli": "somestring", 
+        "dcaeLayer": "somestring", 
+        "dcaeLocationName": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "loggingUrl": "somestring"
-        "nodeKey": "somestring", 
+        "local": true
+        "openStackAvailabilityZone": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "topicNsRoot": "somestring", 
-        "version": "somestring"
+        "subnet": "somestring"
     }
 
 **400**
@@ -615,24 +612,22 @@ Responses
 Success
 
 
-Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+Type: :ref:`DcaeLocation <d_47d80e451933beb623fcf5257867cbcb>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "accessKeyOwner": "somestring"
-        "bridgeAdminTopic": "somestring", 
-        "dmaapName": "somestring", 
-        "drProvUrl": "somestring", 
+        "central": true
+        "clli": "somestring", 
+        "dcaeLayer": "somestring", 
+        "dcaeLocationName": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "loggingUrl": "somestring"
-        "nodeKey": "somestring", 
+        "local": true
+        "openStackAvailabilityZone": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "topicNsRoot": "somestring", 
-        "version": "somestring"
+        "subnet": "somestring"
     }
 
 **400**
@@ -661,14 +656,14 @@ DMAAP
 ~~~~~
 
 
-Endpoint for this instance of DMaaP object containing values for this OpenDCAE deployment
+V2 Endpoint for this instance of DMaaP object containing values for this OpenDCAE deployment
 
 
 
 
 
-POST ``/dmaap``
----------------
+POST ``/dmaap_v2``
+------------------
 
 
 Summary
@@ -712,7 +707,6 @@ Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
         "loggingUrl": "somestring", 
         "nodeKey": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "topicNsRoot": "somestring", 
         "version": "somestring"
     }
@@ -740,8 +734,8 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-GET ``/dmaap``
---------------
+POST ``/dmaap``
+---------------
 
 
 Summary
@@ -754,7 +748,7 @@ Description
 
 .. raw:: html
 
-    returns the `dmaap` object, which contains system wide configuration settings
+    Create a new DMaaP set system wide configuration settings for the *dcaeEnvironment*.  Deprecated with introduction of persistence in 1610.
 
 
 Request
@@ -785,7 +779,6 @@ Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
         "loggingUrl": "somestring", 
         "nodeKey": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "topicNsRoot": "somestring", 
         "version": "somestring"
     }
@@ -813,8 +806,8 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-PUT ``/dmaap``
---------------
+GET ``/dmaap_v2``
+-----------------
 
 
 Summary
@@ -827,7 +820,7 @@ Description
 
 .. raw:: html
 
-    Update system settings for *dcaeEnvironment*.
+    returns the `dmaap` object, which contains system wide configuration settings
 
 
 Request
@@ -858,7 +851,6 @@ Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
         "loggingUrl": "somestring", 
         "nodeKey": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "topicNsRoot": "somestring", 
         "version": "somestring"
     }
@@ -884,32 +876,23 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-  
-DR_NODES
-~~~~~~~~
-
-
-Endpoint for a Data Router Node server
-
-
-
 
 
-POST ``/dr_nodes``
-------------------
+GET ``/dmaap``
+--------------
 
 
 Summary
 +++++++
 
-return DR_Node details
+return dmaap details
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    create a `DR_Node` in a *dcaeLocation*.  Note that multiple `DR_Node`s may exist in the same `dcaeLocation`.
+    returns the `dmaap` object, which contains system wide configuration settings
 
 
 Request
@@ -925,19 +908,22 @@ Responses
 Success
 
 
-Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
+Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "dcaeLocationName": "somestring", 
-        "fqdn": "somestring", 
-        "hostName": "somestring", 
+        "accessKeyOwner": "somestring", 
+        "bridgeAdminTopic": "somestring", 
+        "dmaapName": "somestring", 
+        "drProvUrl": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
+        "loggingUrl": "somestring", 
+        "nodeKey": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true
+        "topicNsRoot": "somestring"
         "version": "somestring"
     }
 
@@ -964,31 +950,21 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-DELETE ``/dr_nodes/{fqdn}``
----------------------------
+PUT ``/dmaap_v2``
+-----------------
 
 
 Summary
 +++++++
 
-No Content
+return dmaap details
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    Delete a single `DR_Node` object.
-
-Parameters
-++++++++++
-
-.. csv-table::
-    :delim: |
-    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
-    :widths: 20, 15, 10, 10, 10, 20, 30
-
-        fqdn | path | Yes | string |  |  | 
+    Update system settings for *dcaeEnvironment*.
 
 
 Request
@@ -1001,44 +977,25 @@ Responses
 **200**
 ^^^^^^^
 
-successful operation
-
-
-Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
-
-**Example:**
-
-.. code-block:: javascript
-
-    {
-        "dcaeLocationName": "somestring", 
-        "fqdn": "somestring", 
-        "hostName": "somestring", 
-        "lastMod": "2015-01-01T15:00:00.000Z", 
-        "status": "EMPTY", 
-        "statusValid": true, 
-        "version": "somestring"
-    }
-
-**204**
-^^^^^^^
-
 Success
 
 
-Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
+Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "dcaeLocationName": "somestring", 
-        "fqdn": "somestring", 
-        "hostName": "somestring", 
+        "accessKeyOwner": "somestring", 
+        "bridgeAdminTopic": "somestring", 
+        "dmaapName": "somestring", 
+        "drProvUrl": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
+        "loggingUrl": "somestring", 
+        "nodeKey": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true
+        "topicNsRoot": "somestring"
         "version": "somestring"
     }
 
@@ -1065,31 +1022,21 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-GET ``/dr_nodes/{fqdn}``
-------------------------
+PUT ``/dmaap``
+--------------
 
 
 Summary
 +++++++
 
-return DR_Node details
+return dmaap details
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    Retrieve a single `DR_Node` object.
-
-Parameters
-++++++++++
-
-.. csv-table::
-    :delim: |
-    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
-    :widths: 20, 15, 10, 10, 10, 20, 30
-
-        fqdn | path | Yes | string |  |  | 
+    Update system settings for *dcaeEnvironment*.
 
 
 Request
@@ -1105,19 +1052,22 @@ Responses
 Success
 
 
-Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
+Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "dcaeLocationName": "somestring", 
-        "fqdn": "somestring", 
-        "hostName": "somestring", 
+        "accessKeyOwner": "somestring", 
+        "bridgeAdminTopic": "somestring", 
+        "dmaapName": "somestring", 
+        "drProvUrl": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
+        "loggingUrl": "somestring", 
+        "nodeKey": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true
+        "topicNsRoot": "somestring"
         "version": "somestring"
     }
 
@@ -1142,10 +1092,19 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
+  
+DR_NODES
+~~~~~~~~
 
 
-GET ``/dr_nodes``
------------------
+Endpoint for a Data Router Node server
+
+
+
+
+
+POST ``/dr_nodes``
+------------------
 
 
 Summary
@@ -1158,7 +1117,7 @@ Description
 
 .. raw:: html
 
-    Returns array of `DR_Node` object array.  Need to add filter by dcaeLocation.
+    create a `DR_Node` in a *dcaeLocation*.  Note that multiple `DR_Node`s may exist in the same `dcaeLocation`.
 
 
 Request
@@ -1186,7 +1145,6 @@ Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
         "hostName": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "version": "somestring"
     }
 
@@ -1213,21 +1171,21 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-PUT ``/dr_nodes/{fqdn}``
-------------------------
+DELETE ``/dr_nodes/{fqdn}``
+---------------------------
 
 
 Summary
 +++++++
 
-return DR_Node details
+No Content
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    Update a single `DR_Node` object.
+    Delete a single `DR_Node` object.
 
 Parameters
 ++++++++++
@@ -1250,6 +1208,27 @@ Responses
 **200**
 ^^^^^^^
 
+successful operation
+
+
+Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "hostName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "status": "EMPTY", 
+        "version": "somestring"
+    }
+
+**204**
+^^^^^^^
+
 Success
 
 
@@ -1265,7 +1244,6 @@ Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
         "hostName": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "version": "somestring"
     }
 
@@ -1290,32 +1268,33 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-  
-DR_PUBS
-~~~~~~~
-
-
-Endpoint for a Data Router client that implements a Publisher
 
 
-
-
-
-POST ``/dr_pubs``
------------------
+GET ``/dr_nodes/{fqdn}``
+------------------------
 
 
 Summary
 +++++++
 
-return DR_Pub details
+return DR_Node details
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    create a DR Publisher in the specified environment.
+    Retrieve a single `DR_Node` object.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        fqdn | path | Yes | string |  |  | 
 
 
 Request
@@ -1331,7 +1310,7 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
 
 **Example:**
 
@@ -1339,13 +1318,11 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
 
     {
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqdn": "somestring", 
+        "hostName": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "version": "somestring"
     }
 
 **400**
@@ -1371,31 +1348,21 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-DELETE ``/dr_pubs/{pubId}``
----------------------------
+GET ``/dr_nodes``
+-----------------
 
 
 Summary
 +++++++
 
-return DR_Pub details
+return DR_Node details
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    delete a DR Publisher in the specified environment. Delete a `DR_Pub` object by pubId
-
-Parameters
-++++++++++
-
-.. csv-table::
-    :delim: |
-    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
-    :widths: 20, 15, 10, 10, 10, 20, 30
-
-        pubId | path | Yes | string |  |  | 
+    Returns array of `DR_Node` object array.  Need to add filter by dcaeLocation.
 
 
 Request
@@ -1408,33 +1375,10 @@ Responses
 **200**
 ^^^^^^^
 
-successful operation
-
-
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
-
-**Example:**
-
-.. code-block:: javascript
-
-    {
-        "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
-        "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
-        "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
-    }
-
-**204**
-^^^^^^^
-
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
 
 **Example:**
 
@@ -1442,13 +1386,11 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
 
     {
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqdn": "somestring", 
+        "hostName": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "version": "somestring"
     }
 
 **400**
@@ -1474,21 +1416,21 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-GET ``/dr_pubs/{pubId}``
+PUT ``/dr_nodes/{fqdn}``
 ------------------------
 
 
 Summary
 +++++++
 
-return DR_Pub details
+return DR_Node details
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    returns a DR Publisher in the specified environment. Gets a `DR_Pub` object by pubId
+    Update a single `DR_Node` object.
 
 Parameters
 ++++++++++
@@ -1498,7 +1440,7 @@ Parameters
     :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
     :widths: 20, 15, 10, 10, 10, 20, 30
 
-        pubId | path | Yes | string |  |  | 
+        fqdn | path | Yes | string |  |  | 
 
 
 Request
@@ -1514,7 +1456,7 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
 
 **Example:**
 
@@ -1522,13 +1464,11 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
 
     {
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqdn": "somestring", 
+        "hostName": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "version": "somestring"
     }
 
 **400**
@@ -1552,10 +1492,19 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
+  
+DR_PUBS
+~~~~~~~
 
 
-GET ``/dr_pubs``
-----------------
+Endpoint for a Data Router client that implements a Publisher
+
+
+
+
+
+POST ``/dr_pubs``
+-----------------
 
 
 Summary
@@ -1568,7 +1517,7 @@ Description
 
 .. raw:: html
 
-    Returns array of  `DR_Pub` objects.  Add filter for feedId.
+    create a DR Publisher in the specified environment.
 
 
 Request
@@ -1596,7 +1545,6 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
         "lastMod": "2015-01-01T15:00:00.000Z", 
         "pubId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "username": "somestring", 
         "userpwd": "somestring"
     }
@@ -1624,8 +1572,8 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-PUT ``/dr_pubs/{pubId}``
-------------------------
+DELETE ``/dr_pubs/{pubId}``
+---------------------------
 
 
 Summary
@@ -1638,7 +1586,7 @@ Description
 
 .. raw:: html
 
-    update a DR Publisher in the specified environment.  Update a `DR_Pub` object by pubId
+    delete a DR Publisher in the specified environment. Delete a `DR_Pub` object by pubId
 
 Parameters
 ++++++++++
@@ -1661,6 +1609,28 @@ Responses
 **200**
 ^^^^^^^
 
+successful operation
+
+
+Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "pubId": "somestring", 
+        "status": "EMPTY", 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**204**
+^^^^^^^
+
 Success
 
 
@@ -1676,7 +1646,6 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
         "lastMod": "2015-01-01T15:00:00.000Z", 
         "pubId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "username": "somestring", 
         "userpwd": "somestring"
     }
@@ -1702,32 +1671,33 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-  
-DR_SUBS
-~~~~~~~
-
-
-Endpoint for a Data Router client that implements a Subscriber
-
-
-
 
 
-POST ``/dr_subs``
------------------
+GET ``/dr_pubs/{pubId}``
+------------------------
 
 
 Summary
 +++++++
 
-return DR_Sub details
+return DR_Pub details
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    Create a  `DR_Sub` object.  
+    returns a DR Publisher in the specified environment. Gets a `DR_Pub` object by pubId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        pubId | path | Yes | string |  |  | 
 
 
 Request
@@ -1755,7 +1725,6 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
         "lastMod": "2015-01-01T15:00:00.000Z", 
         "pubId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "username": "somestring", 
         "userpwd": "somestring"
     }
@@ -1783,31 +1752,21 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-DELETE ``/dr_subs/{subId}``
----------------------------
+GET ``/dr_pubs``
+----------------
 
 
 Summary
 +++++++
 
-return DR_Sub details
+return DR_Pub details
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    Delete a  `DR_Sub` object, selected by subId
-
-Parameters
-++++++++++
-
-.. csv-table::
-    :delim: |
-    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
-    :widths: 20, 15, 10, 10, 10, 20, 30
-
-        subId | path | Yes | string |  |  | 
+    Returns array of  `DR_Pub` objects.  Add filter for feedId.
 
 
 Request
@@ -1835,7 +1794,6 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
         "lastMod": "2015-01-01T15:00:00.000Z", 
         "pubId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "username": "somestring", 
         "userpwd": "somestring"
     }
@@ -1863,21 +1821,21 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-GET ``/dr_subs/{subId}``
+PUT ``/dr_pubs/{pubId}``
 ------------------------
 
 
 Summary
 +++++++
 
-return DR_Sub details
+return DR_Pub details
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    Retrieve a  `DR_Sub` object, selected by subId
+    update a DR Publisher in the specified environment.  Update a `DR_Pub` object by pubId
 
 Parameters
 ++++++++++
@@ -1887,7 +1845,7 @@ Parameters
     :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
     :widths: 20, 15, 10, 10, 10, 20, 30
 
-        subId | path | Yes | string |  |  | 
+        pubId | path | Yes | string |  |  | 
 
 
 Request
@@ -1915,7 +1873,6 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
         "lastMod": "2015-01-01T15:00:00.000Z", 
         "pubId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "username": "somestring", 
         "userpwd": "somestring"
     }
@@ -1941,10 +1898,19 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
+  
+DR_SUBS
+~~~~~~~
 
 
-GET ``/dr_subs``
-----------------
+Endpoint for a Data Router client that implements a Subscriber
+
+
+
+
+
+POST ``/dr_subs``
+-----------------
 
 
 Summary
@@ -1957,7 +1923,7 @@ Description
 
 .. raw:: html
 
-    Returns array of  `DR_Sub` objects.  Add filter for feedId.
+    Create a  `DR_Sub` object.  
 
 
 Request
@@ -1973,19 +1939,27 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`DR_Sub <d_48cf328d246f41e1d11a09251b042f02>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
         "dcaeLocationName": "somestring", 
+        "deliveryURL": "somestring", 
         "feedId": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
+        "subId": "somestring", 
+        "suspended": true, 
+        "use100": true, 
         "username": "somestring", 
         "userpwd": "somestring"
     }
@@ -2013,8 +1987,8 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-PUT ``/dr_subs/{subId}``
-------------------------
+DELETE ``/dr_subs/{subId}``
+---------------------------
 
 
 Summary
@@ -2027,7 +2001,7 @@ Description
 
 .. raw:: html
 
-    Update a  `DR_Sub` object, selected by subId
+    Delete a  `DR_Sub` object, selected by subId
 
 Parameters
 ++++++++++
@@ -2053,19 +2027,27 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`DR_Sub <d_48cf328d246f41e1d11a09251b042f02>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
         "dcaeLocationName": "somestring", 
+        "deliveryURL": "somestring", 
         "feedId": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
+        "subId": "somestring", 
+        "suspended": true, 
+        "use100": true, 
         "username": "somestring", 
         "userpwd": "somestring"
     }
@@ -2091,36 +2073,37 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
-  
-FEEDS
-~~~~~
-
-
-Endpoint for a Data Router Feed
-
-
-
 
 
-POST ``/feeds``
----------------
+GET ``/dr_subs/{subId}``
+------------------------
 
 
 Summary
 +++++++
 
-return Feed details
+return DR_Sub details
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    Create a of  `Feed` object.
-
-
-Request
-+++++++
+    Retrieve a  `DR_Sub` object, selected by subId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        subId | path | Yes | string |  |  | 
+
+
+Request
++++++++
 
 
 Responses
@@ -2132,19 +2115,105 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`DR_Sub <d_48cf328d246f41e1d11a09251b042f02>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
         "dcaeLocationName": "somestring", 
+        "deliveryURL": "somestring", 
         "feedId": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "status": "EMPTY", 
+        "subId": "somestring", 
+        "suspended": true, 
+        "use100": true, 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/dr_subs``
+----------------
+
+
+Summary
++++++++
+
+return DR_Sub details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Returns array of  `DR_Sub` objects.  Add filter for feedId.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Sub <d_48cf328d246f41e1d11a09251b042f02>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "dcaeLocationName": "somestring", 
+        "deliveryURL": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
+        "subId": "somestring", 
+        "suspended": true, 
+        "use100": true, 
         "username": "somestring", 
         "userpwd": "somestring"
     }
@@ -2172,6 +2241,250 @@ Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
 
 
 
+PUT ``/dr_subs/{subId}``
+------------------------
+
+
+Summary
++++++++
+
+return DR_Sub details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Update a  `DR_Sub` object, selected by subId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        subId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Sub <d_48cf328d246f41e1d11a09251b042f02>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "dcaeLocationName": "somestring", 
+        "deliveryURL": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "status": "EMPTY", 
+        "subId": "somestring", 
+        "suspended": true, 
+        "use100": true, 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+  
+FEEDS
+~~~~~
+
+
+Endpoint for a Data Router Feed
+
+
+
+
+
+POST ``/feeds``
+---------------
+
+
+Summary
++++++++
+
+return Feed details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Create a of  `Feed` object.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        useExisting | query | No | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "asprClassification": "somestring", 
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "feedDescription": "somestring", 
+        "feedId": "somestring", 
+        "feedName": "somestring", 
+        "feedVersion": "somestring", 
+        "formatUuid": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "publishURL": "somestring", 
+        "pubs": [
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "status": "EMPTY", 
+        "subs": [
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "subscribeURL": "somestring", 
+        "suspended": true
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
 DELETE ``/feeds/{id}``
 ----------------------
 
@@ -2240,7 +2553,6 @@ Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
                 "lastMod": "2015-01-01T15:00:00.000Z", 
                 "pubId": "somestring", 
                 "status": "EMPTY", 
-                "statusValid": true, 
                 "username": "somestring", 
                 "userpwd": "somestring"
             }, 
@@ -2250,13 +2562,11 @@ Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
                 "lastMod": "2015-01-01T15:00:00.000Z", 
                 "pubId": "somestring", 
                 "status": "EMPTY", 
-                "statusValid": true, 
                 "username": "somestring", 
                 "userpwd": "somestring"
             }
         ], 
         "status": "EMPTY", 
-        "statusValid": true, 
         "subs": [
             {
                 "bytes": [
@@ -2270,7 +2580,6 @@ Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
                 "logURL": "somestring", 
                 "owner": "somestring", 
                 "status": "EMPTY", 
-                "statusValid": true, 
                 "subId": "somestring", 
                 "suspended": true, 
                 "use100": true, 
@@ -2289,7 +2598,6 @@ Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
                 "logURL": "somestring", 
                 "owner": "somestring", 
                 "status": "EMPTY", 
-                "statusValid": true, 
                 "subId": "somestring", 
                 "suspended": true, 
                 "use100": true, 
@@ -2307,21 +2615,88 @@ Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "dcaeLocationName": "somestring", 
+        "asprClassification": "somestring", 
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "feedDescription": "somestring", 
         "feedId": "somestring", 
+        "feedName": "somestring", 
+        "feedVersion": "somestring", 
+        "formatUuid": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "publishURL": "somestring", 
+        "pubs": [
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "subs": [
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "subscribeURL": "somestring", 
+        "suspended": true
     }
 
 **400**
@@ -2399,7 +2774,6 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
         "lastMod": "2015-01-01T15:00:00.000Z", 
         "pubId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "username": "somestring", 
         "userpwd": "somestring"
     }
@@ -2469,21 +2843,88 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "dcaeLocationName": "somestring", 
+        "asprClassification": "somestring", 
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "feedDescription": "somestring", 
         "feedId": "somestring", 
+        "feedName": "somestring", 
+        "feedVersion": "somestring", 
+        "formatUuid": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "publishURL": "somestring", 
+        "pubs": [
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "subs": [
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "subscribeURL": "somestring", 
+        "suspended": true
     }
 
 **400**
@@ -2549,21 +2990,88 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "dcaeLocationName": "somestring", 
+        "asprClassification": "somestring", 
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "feedDescription": "somestring", 
         "feedId": "somestring", 
+        "feedName": "somestring", 
+        "feedVersion": "somestring", 
+        "formatUuid": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "publishURL": "somestring", 
+        "pubs": [
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "subs": [
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "subscribeURL": "somestring", 
+        "suspended": true
     }
 
 **400**
@@ -2643,7 +3151,6 @@ Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
         "loggingUrl": "somestring", 
         "nodeKey": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "topicNsRoot": "somestring", 
         "version": "somestring"
     }
@@ -2687,14 +3194,14 @@ POST ``/mr_clients``
 Summary
 +++++++
 
-return MR_Client details
+Associate an MR_Client object to a Topic
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    Create a  `MR_Client` object.
+    Create a  `MR_Client` object.The `dcaeLocation` attribute is used to match an `MR_Cluster` object with the same value, with the intent of localizing message traffic.  In legacy implementation, the `clientRole` is granted appropriate permission in AAF.  Newer implementions may instead specify an AAF Identity, which will be added to the appropriate `Topic` role.
 
 
 Request
@@ -2710,21 +3217,25 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
+        "action": [
+            "somestring", 
+            "somestring"
+        ], 
+        "clientIdentity": "somestring", 
+        "clientRole": "somestring", 
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqtn": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "mrClientId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "topicURL": "somestring"
     }
 
 **400**
@@ -2757,7 +3268,7 @@ DELETE ``/mr_clients/{subId}``
 Summary
 +++++++
 
-return MR_Client details
+Delete an MR_Client object
 
 Description
 +++++++++++
@@ -2801,13 +3312,13 @@ Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
             "somestring", 
             "somestring"
         ], 
+        "clientIdentity": "somestring", 
         "clientRole": "somestring", 
         "dcaeLocationName": "somestring", 
         "fqtn": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
         "mrClientId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
         "topicURL": "somestring"
     }
 
@@ -2817,21 +3328,25 @@ Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
+        "action": [
+            "somestring", 
+            "somestring"
+        ], 
+        "clientIdentity": "somestring", 
+        "clientRole": "somestring", 
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqtn": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "mrClientId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "topicURL": "somestring"
     }
 
 **400**
@@ -2887,21 +3402,25 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
+        "action": [
+            "somestring", 
+            "somestring"
+        ], 
+        "clientIdentity": "somestring", 
+        "clientRole": "somestring", 
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqtn": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "mrClientId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "topicURL": "somestring"
     }
 
 **400**
@@ -2967,21 +3486,25 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
+        "action": [
+            "somestring", 
+            "somestring"
+        ], 
+        "clientIdentity": "somestring", 
+        "clientRole": "somestring", 
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqtn": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "mrClientId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "topicURL": "somestring"
     }
 
 **400**
@@ -3014,7 +3537,7 @@ PUT ``/mr_clients/{clientId}``
 Summary
 +++++++
 
-return MR_Client details
+Update an MR_Client object
 
 Description
 +++++++++++
@@ -3047,21 +3570,25 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
+        "action": [
+            "somestring", 
+            "somestring"
+        ], 
+        "clientIdentity": "somestring", 
+        "clientRole": "somestring", 
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqtn": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "mrClientId": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "topicURL": "somestring"
     }
 
 **400**
@@ -3126,7 +3653,7 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
 
 **Example:**
 
@@ -3134,13 +3661,14 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
 
     {
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqdn": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "replicationGroup": "somestring", 
+        "sourceReplicationPort": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "targetReplicationPort": "somestring"
+        "topicPort": "somestring", 
+        "topicProtocol": "somestring"
     }
 
 **400**
@@ -3215,13 +3743,11 @@ Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
     {
         "dcaeLocationName": "somestring", 
         "fqdn": "somestring", 
-        "hosts": [
-            "somestring", 
-            "somestring"
-        ], 
         "lastMod": "2015-01-01T15:00:00.000Z", 
+        "replicationGroup": "somestring", 
+        "sourceReplicationPort": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true
+        "targetReplicationPort": "somestring"
         "topicPort": "somestring", 
         "topicProtocol": "somestring"
     }
@@ -3232,7 +3758,7 @@ Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
 
 **Example:**
 
@@ -3240,13 +3766,14 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
 
     {
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqdn": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "replicationGroup": "somestring", 
+        "sourceReplicationPort": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "targetReplicationPort": "somestring"
+        "topicPort": "somestring", 
+        "topicProtocol": "somestring"
     }
 
 **400**
@@ -3312,7 +3839,7 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
 
 **Example:**
 
@@ -3320,13 +3847,14 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
 
     {
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqdn": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "replicationGroup": "somestring", 
+        "sourceReplicationPort": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "targetReplicationPort": "somestring"
+        "topicPort": "somestring", 
+        "topicProtocol": "somestring"
     }
 
 **400**
@@ -3382,7 +3910,7 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
 
 **Example:**
 
@@ -3390,13 +3918,14 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
 
     {
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqdn": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "replicationGroup": "somestring", 
+        "sourceReplicationPort": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "targetReplicationPort": "somestring"
+        "topicPort": "somestring", 
+        "topicProtocol": "somestring"
     }
 
 **400**
@@ -3462,7 +3991,7 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
 
 **Example:**
 
@@ -3470,13 +3999,14 @@ Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
 
     {
         "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "fqdn": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "replicationGroup": "somestring", 
+        "sourceReplicationPort": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "targetReplicationPort": "somestring"
+        "topicPort": "somestring", 
+        "topicProtocol": "somestring"
     }
 
 **400**
@@ -3518,14 +4048,24 @@ POST ``/topics``
 Summary
 +++++++
 
-return Topic details
+Create a Topic object
 
 Description
 +++++++++++
 
 .. raw:: html
 
-    Create  `Topic` object.
+    Create  `Topic` object.For convenience, the message body may populate the `clients` array, in which case each entry will be added as an `MR_Client`.  Beginning in ONAP Dublin Release, dbcapi will create two AAF Roles by default, one each for the publisher and subscriber per topic.  MR_Clients can then specify an AAF Identity to be added to the appropriate default Role, avoiding the need to create Role(s) in advance.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        useExisting | query | No | string |  |  | 
 
 
 Request
@@ -3541,21 +4081,59 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "clients": [
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }, 
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }
+        ], 
+        "formatUuid": "somestring", 
+        "fqtn": "somestring", 
+        "fqtnStyle": "FQTN_NOT_SPECIFIED", 
+        "globalMrURL": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "owner": "somestring", 
+        "partitionCount": "somestring", 
+        "publisherRole": "somestring", 
+        "replicationCase": "REPLICATION_NOT_SPECIFIED", 
+        "replicationCount": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "subscriberRole": "somestring", 
+        "tnxEnabled": "somestring", 
+        "topicDescription": "somestring", 
+        "topicName": "somestring", 
+        "version": "somestring"
     }
 
 **400**
@@ -3628,23 +4206,19 @@ Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
 .. code-block:: javascript
 
     {
-        "bytes": [
-            "somestring", 
-            "somestring"
-        ], 
         "clients": [
             {
                 "action": [
                     "somestring", 
                     "somestring"
                 ], 
+                "clientIdentity": "somestring", 
                 "clientRole": "somestring", 
                 "dcaeLocationName": "somestring", 
                 "fqtn": "somestring", 
                 "lastMod": "2015-01-01T15:00:00.000Z", 
                 "mrClientId": "somestring", 
                 "status": "EMPTY", 
-                "statusValid": true, 
                 "topicURL": "somestring"
             }, 
             {
@@ -3652,13 +4226,13 @@ Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
                     "somestring", 
                     "somestring"
                 ], 
+                "clientIdentity": "somestring", 
                 "clientRole": "somestring", 
                 "dcaeLocationName": "somestring", 
                 "fqtn": "somestring", 
                 "lastMod": "2015-01-01T15:00:00.000Z", 
                 "mrClientId": "somestring", 
                 "status": "EMPTY", 
-                "statusValid": true, 
                 "topicURL": "somestring"
             }
         ], 
@@ -3667,11 +4241,13 @@ Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
         "fqtnStyle": "FQTN_NOT_SPECIFIED", 
         "globalMrURL": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "numClients": 1, 
         "owner": "somestring", 
+        "partitionCount": "somestring", 
+        "publisherRole": "somestring", 
         "replicationCase": "REPLICATION_NOT_SPECIFIED", 
+        "replicationCount": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true
+        "subscriberRole": "somestring"
         "tnxEnabled": "somestring", 
         "topicDescription": "somestring", 
         "topicName": "somestring", 
@@ -3684,21 +4260,59 @@ Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "clients": [
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }, 
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }
+        ], 
+        "formatUuid": "somestring", 
+        "fqtn": "somestring", 
+        "fqtnStyle": "FQTN_NOT_SPECIFIED", 
+        "globalMrURL": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "owner": "somestring", 
+        "partitionCount": "somestring", 
+        "publisherRole": "somestring", 
+        "replicationCase": "REPLICATION_NOT_SPECIFIED", 
+        "replicationCount": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "subscriberRole": "somestring", 
+        "tnxEnabled": "somestring", 
+        "topicDescription": "somestring", 
+        "topicName": "somestring", 
+        "version": "somestring"
     }
 
 **400**
@@ -3764,21 +4378,59 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "clients": [
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }, 
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }
+        ], 
+        "formatUuid": "somestring", 
+        "fqtn": "somestring", 
+        "fqtnStyle": "FQTN_NOT_SPECIFIED", 
+        "globalMrURL": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "owner": "somestring", 
+        "partitionCount": "somestring", 
+        "publisherRole": "somestring", 
+        "replicationCase": "REPLICATION_NOT_SPECIFIED", 
+        "replicationCount": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "subscriberRole": "somestring", 
+        "tnxEnabled": "somestring", 
+        "topicDescription": "somestring", 
+        "topicName": "somestring", 
+        "version": "somestring"
     }
 
 **400**
@@ -3834,21 +4486,59 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "clients": [
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }, 
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }
+        ], 
+        "formatUuid": "somestring", 
+        "fqtn": "somestring", 
+        "fqtnStyle": "FQTN_NOT_SPECIFIED", 
+        "globalMrURL": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "owner": "somestring", 
+        "partitionCount": "somestring", 
+        "publisherRole": "somestring", 
+        "replicationCase": "REPLICATION_NOT_SPECIFIED", 
+        "replicationCount": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "subscriberRole": "somestring", 
+        "tnxEnabled": "somestring", 
+        "topicDescription": "somestring", 
+        "topicName": "somestring", 
+        "version": "somestring"
     }
 
 **400**
@@ -3914,21 +4604,59 @@ Responses
 Success
 
 
-Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
 
 **Example:**
 
 .. code-block:: javascript
 
     {
-        "dcaeLocationName": "somestring", 
-        "feedId": "somestring", 
+        "clients": [
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }, 
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }
+        ], 
+        "formatUuid": "somestring", 
+        "fqtn": "somestring", 
+        "fqtnStyle": "FQTN_NOT_SPECIFIED", 
+        "globalMrURL": "somestring", 
         "lastMod": "2015-01-01T15:00:00.000Z", 
-        "pubId": "somestring", 
+        "owner": "somestring", 
+        "partitionCount": "somestring", 
+        "publisherRole": "somestring", 
+        "replicationCase": "REPLICATION_NOT_SPECIFIED", 
+        "replicationCount": "somestring", 
         "status": "EMPTY", 
-        "statusValid": true, 
-        "username": "somestring", 
-        "userpwd": "somestring"
+        "subscriberRole": "somestring", 
+        "tnxEnabled": "somestring", 
+        "topicDescription": "somestring", 
+        "topicName": "somestring", 
+        "version": "somestring"
     }
 
 **400**
@@ -3983,6 +4711,7 @@ BrTopic Model Structure
 
         brSource | No | string |  |  | 
         brTarget | No | string |  |  | 
+        mmAgentName | No | string |  |  | 
         topicCount | No | integer | int32 |  | 
 
 .. _d_d15e2cee407536866c875375e3f705e0:
@@ -3998,9 +4727,8 @@ DR_Node Model Structure
         dcaeLocationName | No | string |  |  | 
         fqdn | No | string |  |  | 
         hostName | No | string |  |  | 
-        lastMod | No | string | date-time |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
         status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
-        statusValid | No | boolean |  |  | 
         version | No | string |  |  | 
 
 .. _d_e926d3fa8701e0cc9c8ed1761b3255cd:
@@ -4015,10 +4743,9 @@ DR_Pub Model Structure
 
         dcaeLocationName | No | string |  |  | 
         feedId | No | string |  |  | 
-        lastMod | No | string | date-time |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
         pubId | No | string |  |  | 
         status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
-        statusValid | No | boolean |  |  | 
         username | No | string |  |  | 
         userpwd | No | string |  |  | 
 
@@ -4036,11 +4763,10 @@ DR_Sub Model Structure
         dcaeLocationName | No | string |  |  | 
         deliveryURL | No | string |  |  | 
         feedId | No | string |  |  | 
-        lastMod | No | string | date-time |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
         logURL | No | string |  |  | 
         owner | No | string |  |  | 
         status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
-        statusValid | No | boolean |  |  | 
         subId | No | string |  |  | 
         suspended | No | boolean |  |  | 
         use100 | No | boolean |  |  | 
@@ -4061,11 +4787,10 @@ DcaeLocation Model Structure
         clli | No | string |  |  | 
         dcaeLayer | No | string |  |  | 
         dcaeLocationName | No | string |  |  | 
-        lastMod | No | string | date-time |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
         local | No | boolean |  |  | 
         openStackAvailabilityZone | No | string |  |  | 
         status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
-        statusValid | No | boolean |  |  | 
         subnet | No | string |  |  | 
 
 .. _d_4ea0e7758a1f8502222793e4a13b04f7:
@@ -4082,11 +4807,10 @@ Dmaap Model Structure
         bridgeAdminTopic | No | string |  |  | 
         dmaapName | No | string |  |  | 
         drProvUrl | No | string |  |  | 
-        lastMod | No | string | date-time |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
         loggingUrl | No | string |  |  | 
         nodeKey | No | string |  |  | 
         status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
-        statusValid | No | boolean |  |  | 
         topicNsRoot | No | string |  |  | 
         version | No | string |  |  | 
 
@@ -4107,13 +4831,12 @@ Feed Model Structure
         feedName | No | string |  |  | 
         feedVersion | No | string |  |  | 
         formatUuid | No | string |  |  | 
-        lastMod | No | string | date-time |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
         logURL | No | string |  |  | 
         owner | No | string |  |  | 
         publishURL | No | string |  |  | 
         pubs | No | array of :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>` |  |  | 
         status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
-        statusValid | No | boolean |  |  | 
         subs | No | array of :ref:`DR_Sub <d_48cf328d246f41e1d11a09251b042f02>` |  |  | 
         subscribeURL | No | string |  |  | 
         suspended | No | boolean |  |  | 
@@ -4128,15 +4851,15 @@ MR_Client Model Structure
     :header: "Name", "Required", "Type", "Format", "Properties", "Description"
     :widths: 20, 10, 15, 15, 30, 25
 
-        action | No | array of string |  |  | 
-        clientRole | No | string |  |  | 
-        dcaeLocationName | No | string |  |  | 
-        fqtn | No | string |  |  | 
-        lastMod | No | string | date-time |  | 
-        mrClientId | No | string |  |  | 
+        action | No | array of string |  |  | one or more actions from the set ('pub', 'sub', 'view') for which this client needs Permission
+        clientIdentity | No | string |  |  | an AAF identity to be associated to an appropriate topic Role
+        clientRole | No | string |  |  | an AAF Role to be granted an appropriate Permission.  If specified, takes precedence over clientIdentity, for backwards compatibility.
+        dcaeLocationName | No | string |  |  | a tag indicating a logical deployment site
+        fqtn | No | string |  |  | Fully Qualified Topic Name constructed by dbcapi
+        lastMod | No | string | date-time |  | datestamp for last update to this object
+        mrClientId | No | string |  |  | a unique identifier generated by dbcapi for this client
         status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
-        statusValid | No | boolean |  |  | 
-        topicURL | No | string |  |  | 
+        topicURL | No | string |  |  | the URL for a MR instance - typically in the same dcaeLocation - that this client should use to access the topic
 
 .. _d_eec7176a0080debe1b19c2dad2e97c24:
 
@@ -4150,10 +4873,11 @@ MR_Cluster Model Structure
 
         dcaeLocationName | No | string |  |  | 
         fqdn | No | string |  |  | 
-        hosts | No | array of string |  |  | 
         lastMod | No | string | date-time |  | 
+        replicationGroup | No | string |  |  | 
+        sourceReplicationPort | No | string |  |  | 
         status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
-        statusValid | No | boolean |  |  | 
+        targetReplicationPort | No | string |  |  | 
         topicPort | No | string |  |  | 
         topicProtocol | No | string |  |  | 
 
@@ -4167,29 +4891,14 @@ MirrorMaker Model Structure
     :header: "Name", "Required", "Type", "Format", "Properties", "Description"
     :widths: 20, 10, 15, 15, 30, 25
 
-        lastMod | No | string | date-time |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
         mmName | No | string |  |  | 
         sourceCluster | No | string |  |  | 
         status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
-        statusValid | No | boolean |  |  | 
         targetCluster | No | string |  |  | 
         topicCount | No | integer | int32 |  | 
         topics | No | array of string |  |  | 
-        vectors | No | array of :ref:`ReplicationVector <d_56f2d64ab7d8ae64594cda45cf85f918>` |  |  | 
-
-.. _d_56f2d64ab7d8ae64594cda45cf85f918:
-
-ReplicationVector Model Structure
----------------------------------
-
-.. csv-table::
-    :delim: |
-    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
-    :widths: 20, 10, 15, 15, 30, 25
-
-        fqtn | No | string |  |  | 
-        sourceCluster | No | string |  |  | 
-        targetCluster | No | string |  |  | 
+        whitelistUpdateJSON | No | string |  |  | 
 
 .. _d_2e99841971da81b9d240071b86bf168d:
 
@@ -4201,20 +4910,21 @@ Topic Model Structure
     :header: "Name", "Required", "Type", "Format", "Properties", "Description"
     :widths: 20, 10, 15, 15, 30, 25
 
-        bytes | No | array of string |  |  | 
-        clients | No | array of :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>` |  |  | 
-        formatUuid | No | string |  |  | 
-        fqtn | No | string |  |  | 
-        fqtnStyle | No | string |  | {'enum': ['FQTN_NOT_SPECIFIED', 'FQTN_LEGACY_FORMAT', 'FQTN_PROJECTID_FORMAT', 'FQTN_PROJECTID_VERSION_FORMAT']} | 
-        globalMrURL | No | string |  |  | 
-        lastMod | No | string | date-time |  | 
-        numClients | No | integer | int32 |  | 
-        owner | No | string |  |  | 
-        replicationCase | No | string |  | {'enum': ['REPLICATION_NOT_SPECIFIED', 'REPLICATION_NONE', 'REPLICATION_EDGE_TO_CENTRAL', 'REPLICATION_EDGE_TO_CENTRAL_TO_GLOBAL', 'REPLICATION_CENTRAL_TO_EDGE', 'REPLICATION_CENTRAL_TO_GLOBAL', 'REPLICATION_GLOBAL_TO_CENTRAL', 'REPLICATION_GLOBAL_TO_CENTRAL_TO_EDGE']} | 
+        clients | No | array of :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>` |  |  | an array of `MR_Client` objects associated to this `Topic`
+        formatUuid | No | string |  |  | a reference to an identifier that describes a data format used for this `Topic`
+        fqtn | No | string |  |  | Fully Qualified Topic Name constructed by dbcapi, following the rules for `fqtnStyle`
+        fqtnStyle | No | string |  | {'enum': ['FQTN_NOT_SPECIFIED', 'FQTN_LEGACY_FORMAT', 'FQTN_PROJECTID_FORMAT', 'FQTN_PROJECTID_VERSION_FORMAT']} | the construction rule for the `fqtn` field
+        globalMrURL | No | string |  |  | the URL of an outside MR instance
+        lastMod | No | string | date-time |  | datestamp for last update to this object
+        owner | No | string |  |  | a label used to identify who requested this `Topic` to be provisioned.  In the future this may be an AAF Identity.
+        partitionCount | No | string |  |  | the kafka attribute for specifying the number of partitions
+        publisherRole | No | string |  |  | a value generated by dbcapi, this AAF Role has permission to publish to this `Topic`
+        replicationCase | No | string |  | {'enum': ['REPLICATION_NOT_SPECIFIED', 'REPLICATION_NONE', 'REPLICATION_EDGE_TO_CENTRAL', 'REPLICATION_EDGE_TO_CENTRAL_TO_GLOBAL', 'REPLICATION_CENTRAL_TO_EDGE', 'REPLICATION_CENTRAL_TO_GLOBAL', 'REPLICATION_GLOBAL_TO_CENTRAL', 'REPLICATION_GLOBAL_TO_CENTRAL_TO_EDGE', 'REPLICATION_EDGE_TO_FQDN', 'REPLICATION_FQDN_TO_EDGE', 'REPLICATION_FQDN_TO_GLOBAL', 'REPLICATION_GLOBAL_TO_FQDN', 'REPLICATION_EDGE_TO_FQDN_TO_GLOBAL', 'REPLICATION_GLOBAL_TO_FQDN_TO_EDGE']} | An indicator for how this `Topic` should be replicated when there are more than one `MR_Cluster` instances
+        replicationCount | No | string |  |  | the kafka attribute for specifying replication within an `MR_Cluster` instance
         status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
-        statusValid | No | boolean |  |  | 
+        subscriberRole | No | string |  |  | a value generated by dbcapi, this AAF Role has permission to subscribe to this `Topic`
         tnxEnabled | No | string |  |  | 
-        topicDescription | No | string |  |  | 
-        topicName | No | string |  |  | 
-        version | No | string |  |  | 
+        topicDescription | No | string |  |  | a description of what this Topic is used for
+        topicName | No | string |  |  | the short name used by humans, and utilized to construct the `FQTN`
+        version | No | string |  |  | a hook for any versioning needed for managing a `Topic` over time
 
diff --git a/pom.xml b/pom.xml
index 36a3128..8db05b1 100644 (file)
--- a/pom.xml
+++ b/pom.xml
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                <jettyVersion>9.3.8.RC0</jettyVersion> 
                <eelf.version>1.0.0</eelf.version>
-               <artifact.version>1.0.18-SNAPSHOT</artifact.version>
+               <artifact.version>1.0.20-SNAPSHOT</artifact.version>
                <!-- SONAR -->
                <jacoco.version>0.7.7.201606060606</jacoco.version>
                <sonar-jacoco-listeners.version>3.2</sonar-jacoco-listeners.version>
index edc522e..2ebf403 100644 (file)
@@ -3,13 +3,14 @@
  * org.onap.dmaap
  * ================================================================================
  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2018 IBM.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -42,15 +43,15 @@ import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
  * this service uses the AAF Lur object to lookup identities and perms
  */
 public class AafLurService extends BaseLoggingClass {
-       
-       static Logger logger = Logger.getLogger(AafLurService.class.getName());
 
-       
-        private static AAFConHttp aafcon;
-        private static AAFLurPerm aafLur;
-        private static AAFAuthn<?> aafAuthn;
+       static Logger log = Logger.getLogger(AafLurService.class.getName());
+
+
+       private static AAFConHttp aafcon;
+       private static AAFLurPerm aafLur;
+       private static AAFAuthn<?> aafAuthn;
+
 
-       
        /*
         * singleton pattern suggested by AAF
         */
@@ -58,7 +59,7 @@ public class AafLurService extends BaseLoggingClass {
        private AafLurService() {}
 
 
-       
+
        private static void init( Access myAccess ) throws APIException, CadiException, LocatorException {
                appLogger.info( "myAccess=" + myAccess );
                try {
@@ -66,54 +67,53 @@ public class AafLurService extends BaseLoggingClass {
                } catch ( CadiException | LocatorException e) {
                        appLogger.error( "Failure of AAFConHttp: " + e.getMessage() );
                        errorLogger.error( "Failure of AAFConHttp: " + e.getMessage() );
-                       logger.error(e);
-                       e.printStackTrace();
+                       log.error(e);
+
                        throw e;
-               } 
+               }
                try {
                        aafLur = aafcon.newLur();
                } catch ( CadiException  e) {
                        appLogger.error( "Failure of newLur(): " + e.getMessage() );
                        errorLogger.error( "Failure of newLur(): " + e.getMessage() );
-                       logger.error(e);
-                       e.printStackTrace();
+                       log.error(e);
+
                        throw e;
-               } 
-               aafAuthn = aafcon.newAuthn( aafLur ); 
+               }
+               aafAuthn = aafcon.newAuthn( aafLur );
        }
-       
+
        public static synchronized AafLurService getInstance( Access myAccess ) throws APIException, CadiException, LocatorException{
                if ( singleton == null ) {
                        singleton = new AafLurService();
                        try {
                                init( myAccess );
                        } catch (APIException | CadiException | LocatorException e) {
-                               // TODO Auto-generated catch block
-                               logger.error(e);
-                               e.printStackTrace();
+
+                               log.error(e);
                                throw e;
-                       } 
-                       
+                       }
+
                }
                return singleton;
        }
-       
+
 
        public boolean checkPerm(String ns, String fqi, String pwd, DmaapPerm p) throws IOException, CadiException {
 
                boolean rc = false;
-               
+
                if ( aafAuthn == null ) {
                        appLogger.error( "AafLurService: aafAuthn not set as expected.");
                        return rc;
                }
-               
+
                String ok = aafAuthn.validate( fqi,  pwd );
                if ( ok != null ) {
                        appLogger.info( "FAILED validation of fqi=" + fqi + "with response:" + ok );
                        return rc;
-               }       
-               
+               }
+
                Principal principal = new UnAuthPrincipal( fqi );
                // if we pass ns as first arg to AAFPermission constructor it gets prpended to the instance...
                // as in ns|instance|type|action.   we don't want that.
@@ -127,7 +127,7 @@ public class AafLurService extends BaseLoggingClass {
                if (rc == flag ) {
                        return rc;
                }
-               
+
                List<Permission> perms = new ArrayList<Permission>();
                aafLur.fishAll( principal,  perms);
                String key = aafPerm.getKey();
@@ -138,10 +138,8 @@ public class AafLurService extends BaseLoggingClass {
                                appLogger.info( principal + " has non-matching perm " + prm.getKey() );
                        }
                }
-               
-               
+
                return rc;
-       
-               
+
        }
 }
diff --git a/src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java
new file mode 100644 (file)
index 0000000..aa4fb89
--- /dev/null
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+
+public class AafNamespace extends AafObject  {
+       static final Logger logger = Logger.getLogger(AafNamespace.class);
+       
+       private String  name;
+       private ArrayList<String>       admin;
+       private ArrayList<String>       responsible;
+       
+       // in some environments, an AAF Namespace must be owned by a human.
+       // So, when needed, this var can be set via a property
+       static private String NsOwnerIdentity;
+       
+       public AafNamespace(String ns, String identity ) {
+               super();
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               NsOwnerIdentity = p.getProperty( "aaf.NsOwnerIdentity", "");
+               this.admin = new ArrayList<String>();
+               this.responsible = new ArrayList<String>();
+               
+               this.name = ns;
+               this.admin.add( identity );
+               this.responsible.add( NsOwnerIdentity );
+       }
+       public void setName( String ns ) {
+               this.name = ns;
+       }
+       public String getName() {
+               return name;
+       }
+       public ArrayList<String> getAdmin() {
+               return admin;
+       }
+       public void setAdmin(ArrayList<String> admin) {
+               this.admin = admin;
+       }
+       public ArrayList<String> getResponsible() {
+               return responsible;
+       }
+       public void setResponsible(ArrayList<String> responsible) {
+               this.responsible = responsible;
+       }
+
+
+       // given an Array of Strings, return a String that is a separated list of quoted strings.
+       // e.g. input [ a, b, c ]
+       //       output  "a", "b", "c"
+       private String separatedList( ArrayList<String> list, String sep ) {
+               if (list.isEmpty()) return null;
+               String aList = new String();
+               String delim = "";
+               for( String item: list) {
+                       if( ! item.isEmpty()) {
+                               aList += String.format( "%s\"%s\"", delim, item );
+                               delim = sep;
+                       }
+               }
+               return aList;
+       }
+
+       public String toJSON() {
+
+               String postJSON = String.format(" { \"name\": \"%s\", \"admin\": [", 
+                               this.getName()
+                                );
+               postJSON += separatedList( this.getAdmin(), "," );
+               postJSON += "], \"responsible\":[";
+               postJSON += separatedList( this.getResponsible(), ",");
+               postJSON += "]}";
+               logger.info( "returning JSON: " + postJSON);
+                       
+               return postJSON;
+       }
+       
+       
+       
+       
+}
diff --git a/src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java
new file mode 100644 (file)
index 0000000..6acbefd
--- /dev/null
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import org.apache.log4j.Logger;
+
+
+public class AafRole extends AafObject  {
+       static final Logger logger = Logger.getLogger(AafRole.class);
+       
+       private String  namespace;
+       private String  role;
+       
+       public AafRole(String ns,  String role) {
+               super();
+               this.namespace = ns;
+               this.role = role;
+       }
+       public void setNamespace( String ns ) {
+               this.namespace = ns;
+       }
+       public String getNamespace() {
+               return namespace;
+       }
+       public void setRole(String role) {
+               this.role = role;
+       }
+       public String getRole() {
+               return role;
+       }
+       public String getFullyQualifiedRole() {
+               return namespace + "." + role;
+       }
+
+       public String toJSON() {
+
+               String postJSON = String.format(" { \"name\": \"%s.%s\"}", 
+                               this.getNamespace(), 
+                               this.getRole() );
+               logger.info( "returning JSON: " + postJSON);
+                       
+               return postJSON;
+       }
+       
+       
+       
+       
+}
index 112ab11..253ad11 100644 (file)
@@ -36,9 +36,21 @@ public class AafService extends BaseLoggingClass {
        private AafConnection aaf;
        private ServiceType ctype;
        private String aafURL ;
+       private String identity;
        private boolean useAAF = false;
        
        
+       
+       public String getIdentity() {
+               return identity;
+       }
+
+
+       public void setIdentity(String identity) {
+               this.identity = identity;
+       }
+
+
        private String getCred( boolean wPwd ) {
                String mechIdProperty = null;
                String pwdProperty = null;
@@ -55,7 +67,7 @@ public class AafService extends BaseLoggingClass {
                        logger.error( "Unexpected case for AAF credential type: " + ctype );
                        return null;
                }
-               String user = p.getProperty( mechIdProperty, "noMechId@domain.netset.com" );
+               identity = p.getProperty( mechIdProperty, "noMechId@domain.netset.com" );
 
                String pwd = "";
                String encPwd = p.getProperty( pwdProperty, "notSet" );
@@ -64,14 +76,15 @@ public class AafService extends BaseLoggingClass {
                pwd = decryptor.decrypt(encPwd);
                
                if ( wPwd ) {
-                       return user + ":" + pwd;
+                       return identity + ":" + pwd;
                } else {
-                       return user;
+                       return identity;
                }
                
                
        }
        
+       
        public AafService(ServiceType t ) {
                DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
                aafURL = p.getProperty( "aaf.URL", "https://authentication.domain.netset.com:8095/proxy/");
@@ -92,70 +105,16 @@ public class AafService extends BaseLoggingClass {
        }
        
        public int addPerm(DmaapPerm perm) {
-
-               int rc = -1;
                logger.info( "entry: addPerm() "  );
-               String pURL = aafURL + "authz/perm";
-               logger.info( "addPerm=" + useAAF );
-               if ( useAAF ) {
-                       logger.info( "addPerm: " + perm.toJSON());
-                       rc = aaf.postAaf( perm, pURL );
-               } else {
-                       rc = 201;
-               }
-        switch( rc ) {
-       case 401:
-       case 403:
-                       errorLogger.error(DmaapbcLogMessageEnum.AAF_CREDENTIAL_ERROR,  getCred( false ) );
-               System.exit(1);
-       case 409:
-               logger.warn( "Perm already exists. Possible conflict.");
-               break;
-               
-       case 201:
-               logger.info( "expected response: " + rc);
-               break;
-               default :
-               logger.error( "Unexpected response: " + rc );
-               break;
-        }
-               
-               return rc;
+               return doPost( perm, "authz/perm", 201);
        }
        public int addGrant(DmaapGrant grant ) {
-
-               int rc = -1;
                logger.info( "entry: addGrant() "  );
-
-               String pURL = aafURL + "authz/role/perm";
-               logger.info( "addGrant: useAAF=" + useAAF );
-               if ( useAAF ) {
-                       logger.info( "addGrant: " + grant.toJSON() );
-                       rc = aaf.postAaf( grant, pURL );
-               } else {
-                       rc = 201;
-               }
-               
-        switch( rc ) {
-       case 401:
-       case 403:
-                       errorLogger.error(DmaapbcLogMessageEnum.AAF_CREDENTIAL_ERROR,  getCred( false ) );
-               System.exit(1);
-               break;
-
-       case 409:
-               logger.warn( "Perm already exists. Possible conflict.");
-               break;
-               
-       case 201:
-               logger.info( "expected response" );
-               break;
-               default :
-               logger.error( "Unexpected response: " + rc );
-               break;
-        }
-               
-               return rc;
+               return doPost( grant, "authz/role/perm", 201 );
+       }
+       public int addUserRole( AafUserRole ur ) {
+               logger.info( "entry: addUserRole() "  );
+               return doPost( ur, "authz/userRole", 201 );
        }
 
        public int delGrant( DmaapGrant grant ) {
@@ -191,5 +150,49 @@ public class AafService extends BaseLoggingClass {
                return rc;
        }
 
+       public int addRole(AafRole role) {
+               logger.info( "entry: addRole() "  );
+               return doPost( role, "authz/role", 201 );
+       }
+
+       
+       
+       public int addNamespace(AafNamespace ns) {
+               logger.info( "entry: addNamespace() "  );
+               return doPost( ns, "authz/ns", 201 );
+       }
+
+       
+       private int doPost( AafObject obj, String uri, int expect ) {
+               int rc = -1;
+               logger.info( "entry: doPost() "  );
+               String pURL = aafURL + uri;
+               logger.info( "doPost: useAAF=" + useAAF );
+               if ( useAAF ) {
+                       logger.info( "doPost: " + obj.toJSON());
+                       rc = aaf.postAaf( obj, pURL );
+               } else {
+                       rc = expect;
+               }
+        switch( rc ) {
+       case 401:
+       case 403:
+                       errorLogger.error(DmaapbcLogMessageEnum.AAF_CREDENTIAL_ERROR,  getCred( false ) );
+               System.exit(1);
+       case 409:
+               logger.warn( "Object for " + uri + " already exists. Possible conflict.");
+               break;
+               
 
+               default :
+                       if ( rc == expect ) {
+                               logger.info( "expected response: " + rc);
+                       } else {
+                               logger.error( "Unexpected response: " + rc );
+                       }
+               break;
+        }
+        
+        return rc;
+       }
 }
diff --git a/src/main/java/org/onap/dmaap/dbcapi/aaf/AafUserRole.java b/src/main/java/org/onap/dmaap/dbcapi/aaf/AafUserRole.java
new file mode 100644 (file)
index 0000000..7b4f882
--- /dev/null
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import org.apache.log4j.Logger;
+
+
+public class AafUserRole extends AafObject  {
+       static final Logger logger = Logger.getLogger(AafUserRole.class);
+
+       private String  identity;
+       private String  role;
+
+
+       
+       public AafUserRole(String identity,  String role ) {
+               super();
+               this.identity = identity;
+               this.role = role;
+       }
+
+       public void setRole(String role) {
+               this.role = role;
+       }
+       public String getRole() {
+               return role;
+       }
+
+       public String getIdentity() {
+               return identity;
+       }
+
+       public void setIdentity(String identity) {
+               this.identity = identity;
+       }
+
+       public String toJSON() {
+
+               String postJSON = String.format(" { \"user\": \"%s\", \"role\": \"%s\" }",  
+                               this.getIdentity(), 
+                               this.getRole()
+                               );
+               logger.info( "returning JSON: " + postJSON);
+                       
+               return postJSON;
+       }
+       
+       
+       
+       
+}
index 0c81fa9..5b4d7de 100644 (file)
@@ -3,6 +3,7 @@
   * org.onap.dmaap
  * ================================================================================
  * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2018 IBM.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -38,13 +39,14 @@ import org.onap.dmaap.dbcapi.util.DmaapConfig;
 
 public class AafLurAndFish implements ApiAuthorizationCheckInterface {
        private AafLurService svc;
-       private static String api_namespace;
+       private static String apiNamespace;
+       private static final String ERROR="Error";
        static final Logger logger = Logger.getLogger(AafLurAndFish.class);
        
        AafLurAndFish()  throws AuthenticationErrorException  {
        
                DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
-               api_namespace = p.getProperty( "ApiNamespace", "org.onap.dmaap-bc.api");
+               apiNamespace = p.getProperty( "ApiNamespace", "org.onap.dmaap-bc.api");
 
                String cadiprop = p.getProperty( "cadi.properties", "/opt/app/osaaf/local/org.onap.dmaap-bc.props");
                logger.info( "cadiprops in " + cadiprop );
@@ -58,7 +60,7 @@ public class AafLurAndFish implements ApiAuthorizationCheckInterface {
                        }
                } catch ( IOException e ) {
                        logger.error( "Unable to load " + cadiprop );
-                       logger.error("Error", e);
+                       logger.error(ERROR, e);
                        throw new AuthenticationErrorException( );
                }
                try {
@@ -66,7 +68,7 @@ public class AafLurAndFish implements ApiAuthorizationCheckInterface {
                
                        svc =  AafLurService.getInstance(myAccess);
                } catch (APIException | CadiException | LocatorException e ) {
-                       logger.error("Error", e);
+                       logger.error(ERROR, e);
                        logger.error( e.toString() );
                        throw new AuthenticationErrorException();
                }
@@ -76,13 +78,13 @@ public class AafLurAndFish implements ApiAuthorizationCheckInterface {
        public void check( String mechid, String pwd, DmaapPerm p ) throws AuthenticationErrorException {
        
                try {
-                       boolean resp = svc.checkPerm( api_namespace, mechid, pwd, p );
+                       boolean resp = svc.checkPerm( apiNamespace, mechid, pwd, p );
                        boolean flag = false;
                        if ( resp == flag ) {
                                throw new AuthenticationErrorException();
                        }
                } catch ( IOException | CadiException  e ) { 
-                       logger.error("Error", e);
+                       logger.error(ERROR, e);
                        logger.error( e.toString() );
                        throw new AuthenticationErrorException();
                }
@@ -97,7 +99,7 @@ public class AafLurAndFish implements ApiAuthorizationCheckInterface {
                        alaf.check("mmanager@people.osaaf.org", "demo123456!", p);
                } catch (AuthenticationErrorException aee ) {
                        logger.error( "Check failed for: " + p.toJSON());
-                       System.exit(-1);
+                       System.exit(-1);
                }
                logger.info( "Check succeeded for: " + p.toJSON() );
                
index 26c494f..94d671f 100644 (file)
@@ -3,6 +3,7 @@
  * org.onap.dmaap
  * ================================================================================
  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2018 IBM.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -37,9 +38,6 @@ import javax.net.ssl.HttpsURLConnection;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.log4j.Logger;
 import org.onap.dmaap.dbcapi.aaf.AafDecrypt;
-import org.onap.dmaap.dbcapi.aaf.AafService;
-import org.onap.dmaap.dbcapi.aaf.DecryptionInterface;
-import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType;
 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
 import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
 import org.onap.dmaap.dbcapi.model.ApiError;
@@ -227,13 +225,15 @@ public class MrProvConnection extends BaseLoggingClass{
             } 
             
                } catch (Exception e) {
-            System.err.println("Unable to read response  " );
-            e.printStackTrace();
+                       errorLogger.error("Unable to read response  " );
+           
         }
                finally {
                        try {
                                uc.disconnect();
-                       } catch ( Exception e ) {}
+                       } catch ( Exception e ) {
+                               errorLogger.error("Unable to disconnect");
+                       }
                }
                return new String( rc +": " + responsemessage );
 
index 11b0d15..934b7bb 100644 (file)
@@ -200,22 +200,18 @@ public class DatabaseClass extends BaseLoggingClass {
                        mirrors = new HashMap<>();
                }
                dmaap.init(new Dmaap("0", "", "", "", "", "", "", ""));
-               // check for, and set up initial data, if it isn't already there
+               // force initial read from DB, if it exists
+               @SuppressWarnings("unused")
                Dmaap dmx = dmaap.get();
-               if ("0".equals(dmx.getVersion())) {
-
-                       dmx = new Dmaap("0", "", "", "", "", "", "", "");
-                       dmx.setDmaapName(p.getProperty("DmaapName"));
-                       dmx.setDrProvUrl("https://" + p.getProperty("DR.provhost", "notSet"));
-                       dmx.setTopicNsRoot(p.getProperty("topicNsRoot"));
-                       dmx.setBridgeAdminTopic("DCAE_MM_AGENT");
+               
+               // old code in this spot would read from properties file as part of init.
+               // but all those properties are now set via /dmaap API
 
-                       dmaap.update(dmx);
-               }
                } catch (Exception e) {
                        errorLogger.error("Error", e);
                        errorLogger.error(DmaapbcLogMessageEnum.DB_UPDATE_ERROR, e.getMessage());
                }
+
        }
        
        public static synchronized String getNextClientId() {
index 8e804b2..1b3f824 100644 (file)
@@ -30,8 +30,11 @@ import javax.xml.bind.annotation.XmlRootElement;
 
 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
 
+import io.swagger.annotations.ApiModelProperty;
+
 @XmlRootElement
 public abstract class DmaapObject extends BaseLoggingClass {
+       @ApiModelProperty( value = "datestamp for last update to this object")
        protected Date lastMod;
        protected       DmaapObject_Status      status;
        
@@ -81,6 +84,7 @@ public abstract class DmaapObject extends BaseLoggingClass {
                }
        }
        
+       @ApiModelProperty( hidden=true )
        public boolean isStatusValid() {
                if ( this.status == DmaapObject_Status.VALID ) {
                        return true;
index 580800c..f182fd6 100644 (file)
@@ -26,15 +26,25 @@ import javax.xml.bind.annotation.XmlRootElement;
 
 import org.onap.dmaap.dbcapi.database.DatabaseClass;
 
+import io.swagger.annotations.ApiModelProperty;
+
 @XmlRootElement
 public class MR_Client extends DmaapObject {
 
+       @ApiModelProperty( value="a tag indicating a logical deployment site")
        private String dcaeLocationName;
+       @ApiModelProperty( value="the URL for a MR instance - typically in the same dcaeLocation - that this client should use to access the topic")
        private String  topicURL;
+       @ApiModelProperty( value="Fully Qualified Topic Name constructed by dbcapi")
        private String fqtn;
+       @ApiModelProperty( value="an AAF Role to be granted an appropriate Permission.  If specified, takes precedence over clientIdentity, for backwards compatibility.")
        private String clientRole;
+       @ApiModelProperty( value="one or more actions from the set (\"pub\", \"sub\", \"view\") for which this client needs Permission")
        private String[] action;
+       @ApiModelProperty( value="a unique identifier generated by dbcapi for this client")
        private String mrClientId;
+       @ApiModelProperty( value="an AAF identity to be associated to an appropriate topic Role")
+       private String clientIdentity;
        
 
        public MR_Client() {
@@ -97,7 +107,24 @@ public class MR_Client extends DmaapObject {
        public void setAction(String[] action) {
                this.action = action;
        }
-
+       
+       @ApiModelProperty( hidden=true )
+       public boolean isPublisher() {
+               return hasAction( "pub");
+       }
+       @ApiModelProperty( hidden=true )
+       public boolean isSubscriber() {
+               return hasAction( "sub");
+       }
+       
+       public boolean hasAction( String val ) {
+               for (String s: this.action) {
+                       if ( s!= null && s.equals(val)) {
+                               return true;
+                       }
+               }
+               return false;
+       }
        public String getMrClientId() {
                return mrClientId;
        }
@@ -115,5 +142,21 @@ public class MR_Client extends DmaapObject {
        public void setTopicURL(String topicURL) {
                this.topicURL = topicURL;
        }
+
+       public String getClientIdentity() {
+               return clientIdentity;
+       }
+
+       public void setClientIdentity(String clientIdentity) {
+               this.clientIdentity = clientIdentity;
+       }
+       public boolean hasClientIdentity() {
+               if ( this.clientIdentity == null || this.clientIdentity.isEmpty() ) {
+                       return false;
+               } else {
+                       return true;
+               }
+       }
+
        
 }
index 6364382..7eea5f1 100644 (file)
@@ -30,29 +30,54 @@ import javax.xml.bind.annotation.XmlRootElement;
 
 import org.onap.dmaap.dbcapi.util.DmaapConfig;
 
+import io.swagger.annotations.ApiModelProperty;
+
 import org.onap.dmaap.dbcapi.service.DmaapService;
+import org.onap.dmaap.dbcapi.service.TopicService;
 
 
 @XmlRootElement
 public class Topic extends DmaapObject  {
 
+       @ApiModelProperty( value="Fully Qualified Topic Name constructed by dbcapi, following the rules for `fqtnStyle`")
        private String fqtn;
+       @ApiModelProperty( value="the short name used by humans, and utilized to construct the `FQTN`")
        private String topicName;
+       @ApiModelProperty( value="a description of what this Topic is used for")
        private String  topicDescription;
        private String  tnxEnabled;
+       @ApiModelProperty( value="a label used to identify who requested this `Topic` to be provisioned.  In the future this "
+                       + "may be an AAF Identity.")
        private String  owner;
+       @ApiModelProperty( value="a reference to an identifier that describes a data format used for this `Topic`")
        private String  formatUuid;
+       @ApiModelProperty( value="An indicator for how this `Topic` should be replicated when there are more than one `MR_Cluster` instances")
        private ReplicationType replicationCase;  
+       @ApiModelProperty( value="the URL of an outside MR instance")
        private String  globalMrURL;            // optional: URL of global MR to replicate to/from
+       @ApiModelProperty( value="the construction rule for the `fqtn` field")
        private FqtnType  fqtnStyle;
+       @ApiModelProperty( value="a hook for any versioning needed for managing a `Topic` over time")
        private String  version;
-
+       @ApiModelProperty( value="the kafka attribute for specifying the number of partitions")
+       private String  partitionCount;
+       @ApiModelProperty( value="the kafka attribute for specifying replication within an `MR_Cluster` instance")
+       private String  replicationCount;
+       @ApiModelProperty( value="a value generated by dbcapi, this AAF Role has permission to publish to this `Topic`")
+       private String  publisherRole;
+       @ApiModelProperty( value="a value generated by dbcapi, this AAF Role has permission to subscribe to this `Topic`")
+       private String  subscriberRole;
+
+       @ApiModelProperty( value="an array of `MR_Client` objects associated to this `Topic`")
        private ArrayList<MR_Client> clients;
 
 
        
        private static Dmaap dmaap = new DmaapService().getDmaap();
-
+       
+       private static String defaultPartitionCount;
+       private static String defaultReplicationCount;
+       
        // during unit testing, discovered that presence of dots in some values
        // creates an unplanned topic namespace as we compose the FQTN.
        // this may create sensitivity (i.e. 403) for subsequent creation of AAF perms, so best to not allow it 
@@ -118,11 +143,24 @@ public class Topic extends DmaapObject  {
                //this.dcaeLocationName = dcaeLocationName;
                this.tnxEnabled = tnxEnabled;
                this.owner = owner;
+               this.init();
                this.setLastMod();
+               logger.debug( "Topic constructor w args " + this.getLastMod() );
+       }
+       
+       public Topic init() {
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               
+               defaultPartitionCount = p.getProperty( "MR.partitionCount", "2");
+               defaultReplicationCount = p.getProperty( "MR.replicationCount", "1");
+               
                this.setStatus( DmaapObject_Status.NEW );
                this.replicationCase = ReplicationType.Validator("none");
                this.fqtnStyle = FqtnType.Validator("none");
-               logger.debug( "Topic constructor " + this.getLastMod() );
+               this.setPartitionCount( defaultPartitionCount );
+               this.setReplicationCount( defaultReplicationCount );
+               
+               return this;
        }
 
        // expects a String in JSON format, with known fields to populate Topic object
@@ -144,6 +182,7 @@ public class Topic extends DmaapObject  {
                this.setStatus( (String) jsonObj.get( "status" ) );
                this.setReplicationCase( ReplicationType.Validator( (String) jsonObj.get( "replicationCase" ) ));
                this.setFqtnStyle( FqtnType.Validator( (String) jsonObj.get( "fqtnStyle" ) ) );
+               this.setPartitionCount( (String) jsonObj.get("partitionCount"));
 
        }
        public String getFqtn() {
@@ -177,6 +216,18 @@ public class Topic extends DmaapObject  {
        public void setOwner(String owner) {
                this.owner = owner;
        }
+       public String getPartitionCount() {
+               return partitionCount;
+       }
+       public void setPartitionCount(String partitions) {
+               this.partitionCount = partitions;
+       }
+       public String getReplicationCount() {
+               return replicationCount;
+       }
+       public void setReplicationCount(String replicationCount) {
+               this.replicationCount = replicationCount;
+       }
 
 
        public void setClients(ArrayList<MR_Client> clients) {
@@ -187,6 +238,7 @@ public class Topic extends DmaapObject  {
                return clients;
        }
 
+       @ApiModelProperty( hidden=true )
        public int getNumClients() {
                if ( this.clients == null ) {
                        return 0;
@@ -212,13 +264,6 @@ public class Topic extends DmaapObject  {
                return replicationCase;
        }
 
-
-
-       /*
-       public void setReplicationCase(String val) {
-               this.replicationCase = ReplicationType.Validator(val);
-       }
-       */
        
        public void setReplicationCase(ReplicationType t) {
                this.replicationCase = t;
@@ -256,17 +301,34 @@ public class Topic extends DmaapObject  {
 
 
 
+       public String getPublisherRole() {
+               return publisherRole;
+       }
+       public void setPublisherRole(String publisherRole) {
+               this.publisherRole = publisherRole;
+       }
+       public String getSubscriberRole() {
+               return subscriberRole;
+       }
+       public void setSubscriberRole(String subscriberRole) {
+               this.subscriberRole = subscriberRole;
+       }
        public String toProvJSON() {
                StringBuilder str = new StringBuilder();
                str.append("{ \"topicName\": \"");
                str.append( this.getFqtn() );
                str.append( "\", \"topicDescription\": \"");
                str.append( this.getTopicDescription());
-               str.append( "\", \"partitionCount\": \"2\", \"replicationCount\": \"1\" } ");
+               str.append( "\", \"partitionCount\": \"");
+               str.append( this.getPartitionCount());
+               str.append( "\", \"replicationCount\": \"");
+               str.append( this.getReplicationCount());
+               str.append( "\" } ");
+               
                logger.info( str.toString() );
                return str.toString();
        }
-       
+       @ApiModelProperty( hidden=true )
        public byte[] getBytes() {
                return toProvJSON().getBytes(StandardCharsets.UTF_8);
        }
index becc7df..192b63d 100644 (file)
@@ -37,7 +37,6 @@ import javax.ws.rs.core.Response.Status;
 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
 import org.onap.dmaap.dbcapi.model.ApiError;
 import org.onap.dmaap.dbcapi.model.BrTopic;
-import org.onap.dmaap.dbcapi.model.Dmaap;
 import org.onap.dmaap.dbcapi.model.MirrorMaker;
 import org.onap.dmaap.dbcapi.service.ApiService;
 import org.onap.dmaap.dbcapi.service.MirrorMakerService;
@@ -62,7 +61,7 @@ public class BridgeResource extends BaseLoggingClass {
                        + "If detail param is true, list topics names, else just a count is returned.", 
        response = BrTopic.class)
 @ApiResponses( value = {
-    @ApiResponse( code = 200, message = "Success", response = Dmaap.class),
+    @ApiResponse( code = 200, message = "Success", response = BrTopic.class),
     @ApiResponse( code = 400, message = "Error", response = ApiError.class )
 })
        public Response getBridgedTopics(@QueryParam("mmagent") String mmagent,
@@ -150,7 +149,7 @@ public class BridgeResource extends BaseLoggingClass {
                                + "If split param is true, spread whitelist over smaller mmagents.", 
                response = MirrorMaker.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = Dmaap.class),
+           @ApiResponse( code = 200, message = "Success", response = BrTopic.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        public Response putBridgedTopics(@QueryParam("mmagent") String mmagent,
index 41a491d..58a39df 100644 (file)
@@ -38,7 +38,6 @@ import javax.ws.rs.core.Response.Status;
 
 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
 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.service.ApiService;
@@ -63,7 +62,7 @@ public class DR_SubResource extends BaseLoggingClass {
        notes = "Returns array of  `DR_Sub` objects.  Add filter for feedId.", 
        response = DR_Sub.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = DR_Sub.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        public Response getDr_Subs() {
@@ -83,7 +82,7 @@ public class DR_SubResource extends BaseLoggingClass {
        notes = "Create a  `DR_Sub` object.  ", 
        response = DR_Sub.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = DR_Sub.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        public Response addDr_Sub( 
@@ -132,7 +131,7 @@ public class DR_SubResource extends BaseLoggingClass {
        notes = "Update a  `DR_Sub` object, selected by subId", 
        response = DR_Sub.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = DR_Sub.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{subId}")
@@ -173,7 +172,7 @@ public class DR_SubResource extends BaseLoggingClass {
        notes = "Delete a  `DR_Sub` object, selected by subId", 
        response = DR_Sub.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = DR_Sub.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{subId}")
@@ -202,7 +201,7 @@ public class DR_SubResource extends BaseLoggingClass {
        notes = "Retrieve a  `DR_Sub` object, selected by subId", 
        response = DR_Sub.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = DR_Sub.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{subId}")
index 25e6f3a..8cb5336 100644 (file)
@@ -45,7 +45,6 @@ import org.apache.log4j.Logger;
 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
 import org.onap.dmaap.dbcapi.model.ApiError;
 import org.onap.dmaap.dbcapi.model.DcaeLocation;
-import org.onap.dmaap.dbcapi.model.Dmaap;
 import org.onap.dmaap.dbcapi.service.ApiService;
 import org.onap.dmaap.dbcapi.service.DcaeLocationService;
 
@@ -64,7 +63,7 @@ public class DcaeLocationResource extends BaseLoggingClass {
                notes = "Returns array of  `dcaeLocation` objects.  All objects managed by DMaaP are deployed in some `dcaeLocation` which is a unique identifier for an *OpenStack* tenant purposed for a *dcaeLayer*  (ecomp or edge).", 
                response = DcaeLocation.class)
     @ApiResponses( value = {
-        @ApiResponse( code = 200, message = "Success", response = Dmaap.class),
+        @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class),
         @ApiResponse( code = 400, message = "Error", response = ApiError.class )
     })
        public Response getDcaeLocations() {
@@ -82,7 +81,7 @@ public class DcaeLocationResource extends BaseLoggingClass {
                notes = "Create some `dcaeLocation` which is a unique identifier for an *OpenStack* tenant purposed for a *dcaeLayer*  (ecomp or edge).", 
                response = DcaeLocation.class)
     @ApiResponses( value = {
-        @ApiResponse( code = 200, message = "Success", response = Dmaap.class),
+        @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class),
         @ApiResponse( code = 400, message = "Error", response = ApiError.class )
     })
        public Response addDcaeLocation( 
@@ -108,7 +107,7 @@ public class DcaeLocationResource extends BaseLoggingClass {
                notes = "update the openStackAvailabilityZone of a dcaeLocation", 
                response = DcaeLocation.class)
     @ApiResponses( value = {
-        @ApiResponse( code = 200, message = "Success", response = Dmaap.class),
+        @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class),
         @ApiResponse( code = 400, message = "Error", response = ApiError.class )
     })
        @Path("/{locationName}")
@@ -135,9 +134,9 @@ public class DcaeLocationResource extends BaseLoggingClass {
        }
        
        @DELETE
-       @ApiOperation( value = "return dcaeLocation details", notes = "delete a dcaeLocation", response = Dmaap.class)
+       @ApiOperation( value = "return dcaeLocation details", notes = "delete a dcaeLocation", response = DcaeLocation.class)
     @ApiResponses( value = {
-        @ApiResponse( code = 204, message = "Success", response = Dmaap.class),
+        @ApiResponse( code = 204, message = "Success", response = DcaeLocation.class),
         @ApiResponse( code = 400, message = "Error", response = ApiError.class )
     })
        @Path("/{locationName}")
@@ -151,9 +150,9 @@ public class DcaeLocationResource extends BaseLoggingClass {
        }
 
        @GET
-       @ApiOperation( value = "return dcaeLocation details", notes = "Returns a specific `dcaeLocation` object with specified tag", response = Dmaap.class)
+       @ApiOperation( value = "return dcaeLocation details", notes = "Returns a specific `dcaeLocation` object with specified tag", response = DcaeLocation.class)
     @ApiResponses( value = {
-        @ApiResponse( code = 200, message = "Success", response = Dmaap.class),
+        @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class),
         @ApiResponse( code = 400, message = "Error", response = ApiError.class )
     })
        @Path("/{locationName}")
index 97336c1..a7b4f73 100644 (file)
@@ -31,29 +31,20 @@ import javax.jws.WebParam;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
-import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.Context;
 import javax.ws.rs.core.GenericEntity;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriInfo;
-
-import org.apache.log4j.Logger;
-import org.onap.dmaap.dbcapi.authentication.AuthenticationErrorException;
 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
-import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
 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.Topic;
 import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
 import org.onap.dmaap.dbcapi.service.ApiService;
 import org.onap.dmaap.dbcapi.service.FeedService;
@@ -71,7 +62,7 @@ public class FeedResource extends BaseLoggingClass {
        notes = "Returns array of  `Feed` objects.", 
        response = Feed.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = Feed.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        public Response getFeeds(
@@ -96,7 +87,7 @@ public class FeedResource extends BaseLoggingClass {
        notes = "Create a of  `Feed` object.", 
        response = Feed.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = Feed.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        public Response addFeed( 
@@ -151,7 +142,7 @@ public class FeedResource extends BaseLoggingClass {
        notes = "Update a  `Feed` object, specified by id.", 
        response = Feed.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = Feed.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{id}")
@@ -196,7 +187,7 @@ public class FeedResource extends BaseLoggingClass {
        notes = "Delete a  `Feed` object, specified by id.", 
        response = Feed.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 204, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 204, message = "Success", response = Feed.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{id}")
index 8f6a506..a621338 100644 (file)
@@ -42,7 +42,6 @@ import javax.ws.rs.core.Response.Status;
 
 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
 import org.onap.dmaap.dbcapi.model.ApiError;
-import org.onap.dmaap.dbcapi.model.DR_Pub;
 import org.onap.dmaap.dbcapi.model.MR_Client;
 import org.onap.dmaap.dbcapi.model.MR_Cluster;
 import org.onap.dmaap.dbcapi.model.Topic;
@@ -66,7 +65,7 @@ public class MR_ClientResource extends BaseLoggingClass {
        notes = "Returns array of  `MR_Client` objects.", 
        response = MR_Client.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = MR_Client.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        public Response getMr_Clients() {
@@ -80,11 +79,14 @@ public class MR_ClientResource extends BaseLoggingClass {
        }
                
        @POST
-       @ApiOperation( value = "return MR_Client details", 
-       notes = "Create a  `MR_Client` object.", 
+       @ApiOperation( value = "Associate an MR_Client object to a Topic", 
+       notes = "Create a  `MR_Client` object."
+                       + "The `dcaeLocation` attribute is used to match an `MR_Cluster` object with the same value, with the intent of localizing message traffic."
+                       + "  In legacy implementation, the `clientRole` is granted appropriate permission in AAF."
+                       + "  Newer implementions may instead specify an AAF Identity, which will be added to the appropriate `Topic` role.", 
        response = MR_Client.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = MR_Client.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        public Response addMr_Client( 
@@ -95,7 +97,11 @@ public class MR_ClientResource extends BaseLoggingClass {
                try {
                        resp.required( "fqtn", client.getFqtn(), "");
                        resp.required( "dcaeLocationName", client.getDcaeLocationName(), "");
-                       resp.required( "clientRole", client.getClientRole(), "" );
+                       String s = client.getClientRole();
+                       if ( s == null ) {
+                               s = client.getClientIdentity();
+                       }
+                       resp.required( "clientRole or clientIdentity", s, "" );
                        resp.required( "action", client.getAction(), "");
 
                } catch ( RequiredFieldException rfe ) {
@@ -140,11 +146,11 @@ public class MR_ClientResource extends BaseLoggingClass {
        }
                
        @PUT
-       @ApiOperation( value = "return MR_Client details", 
+       @ApiOperation( value = "Update an MR_Client object", 
        notes = "Update a  `MR_Client` object, specified by clientId", 
        response = MR_Client.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = MR_Client.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{clientId}")
@@ -176,11 +182,11 @@ public class MR_ClientResource extends BaseLoggingClass {
        }
                
        @DELETE
-       @ApiOperation( value = "return MR_Client details", 
+       @ApiOperation( value = "Delete an MR_Client object", 
        notes = "Delete a  `MR_Client` object, specified by clientId", 
        response = MR_Client.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 204, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 204, message = "Success", response = MR_Client.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{subId}")
@@ -208,7 +214,7 @@ public class MR_ClientResource extends BaseLoggingClass {
        notes = "Retrieve a  `MR_Client` object, specified by clientId", 
        response = MR_Client.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = MR_Client.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{subId}")
index 41aaead..50ab4db 100644 (file)
@@ -42,7 +42,6 @@ import javax.ws.rs.core.Response.Status;
 
 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
 import org.onap.dmaap.dbcapi.model.ApiError;
-import org.onap.dmaap.dbcapi.model.DR_Pub;
 import org.onap.dmaap.dbcapi.model.MR_Cluster;
 import org.onap.dmaap.dbcapi.service.ApiService;
 import org.onap.dmaap.dbcapi.service.MR_ClientService;
@@ -64,7 +63,7 @@ public class MR_ClusterResource extends BaseLoggingClass {
        notes = "Returns array of  `MR_Cluster` objects.", 
        response = MR_Cluster.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        public Response getMr_Clusters() {
@@ -82,7 +81,7 @@ public class MR_ClusterResource extends BaseLoggingClass {
        notes = "Create an  `MR_Cluster` object.", 
        response = MR_Cluster.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        public Response  addMr_Cluster( 
@@ -109,7 +108,7 @@ public class MR_ClusterResource extends BaseLoggingClass {
        notes = "Update an  `MR_Cluster` object, specified by clusterId.", 
        response = MR_Cluster.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{clusterId}")
@@ -138,7 +137,7 @@ public class MR_ClusterResource extends BaseLoggingClass {
        notes = "Delete an  `MR_Cluster` object, specified by clusterId.", 
        response = MR_Cluster.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 204, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 204, message = "Success", response = MR_Cluster.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{clusterId}")
@@ -164,7 +163,7 @@ public class MR_ClusterResource extends BaseLoggingClass {
        notes = "Retrieve an  `MR_Cluster` object, specified by clusterId.", 
        response = MR_Cluster.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{clusterId}")
index 8ed2558..be1b3ac 100644 (file)
@@ -43,7 +43,6 @@ import javax.ws.rs.core.Response.Status;
 
 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
 import org.onap.dmaap.dbcapi.model.ApiError;
-import org.onap.dmaap.dbcapi.model.DR_Pub;
 import org.onap.dmaap.dbcapi.model.ReplicationType;
 import org.onap.dmaap.dbcapi.model.FqtnType;
 import org.onap.dmaap.dbcapi.model.Topic;
@@ -58,11 +57,16 @@ import org.onap.dmaap.dbcapi.util.DmaapConfig;
 @Authorization
 public class TopicResource extends BaseLoggingClass {
        private static FqtnType defaultTopicStyle;
+       private static String defaultPartitionCount;
+       private static String defaultReplicationCount;
        TopicService mr_topicService = new TopicService();
        
        public TopicResource() {
                DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
-               defaultTopicStyle = FqtnType.Validator( p.getProperty("MR.topicStyle", "FQTN_LEGACY_FORMAT"));
+               defaultTopicStyle = FqtnType.Validator( p.getProperty("MR.topicStyle", "FQTN_LEGACY_FORMAT"));
+               defaultPartitionCount = p.getProperty( "MR.partitionCount", "2");
+               defaultReplicationCount = p.getProperty( "MR.replicationCount", "1");
+               
                logger.info( "Setting defaultTopicStyle=" + defaultTopicStyle );
        }
                
@@ -71,7 +75,7 @@ public class TopicResource extends BaseLoggingClass {
        notes = "Returns array of  `Topic` objects.", 
        response = Topic.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = Topic.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        public Response getTopics() {
@@ -87,11 +91,14 @@ public class TopicResource extends BaseLoggingClass {
        }
                
        @POST
-       @ApiOperation( value = "return Topic details", 
-       notes = "Create  `Topic` object.", 
+       @ApiOperation( value = "Create a Topic object", 
+       notes = "Create  `Topic` object."
+                       + "For convenience, the message body may populate the `clients` array, in which case each entry will be added as an `MR_Client`."
+                       + "  Beginning in ONAP Dublin Release, dbcapi will create two AAF Roles by default, one each for the publisher and subscriber per topic."
+                       + "  MR_Clients can then specify an AAF Identity to be added to the appropriate default Role, avoiding the need to create Role(s) in advance.", 
        response = Topic.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = Topic.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        public Response  addTopic( 
@@ -119,6 +126,14 @@ public class TopicResource extends BaseLoggingClass {
                        logger.info( "setting defaultTopicStyle=" + defaultTopicStyle + " for topic " + topic.getTopicName() );
                        topic.setFqtnStyle( defaultTopicStyle );
                }
+               String pc = topic.getPartitionCount();
+               if ( pc == null ) {
+                       topic.setPartitionCount(defaultPartitionCount);
+               }
+               String rc = topic.getReplicationCount();
+               if ( rc == null ) {
+                       topic.setReplicationCount(defaultReplicationCount);
+               }
                topic.setLastMod();
                Boolean flag = false;
                if (useExisting != null) {
@@ -137,7 +152,7 @@ public class TopicResource extends BaseLoggingClass {
        notes = "Update a  `Topic` object, identified by topicId", 
        response = Topic.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = Topic.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{topicId}")
@@ -157,7 +172,7 @@ public class TopicResource extends BaseLoggingClass {
        notes = "Delete a  `Topic` object, identified by topicId", 
        response = Topic.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 204, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 204, message = "Success", response = Topic.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{topicId}")
@@ -186,7 +201,7 @@ public class TopicResource extends BaseLoggingClass {
        notes = "Retrieve a  `Topic` object, identified by topicId", 
        response = Topic.class)
        @ApiResponses( value = {
-           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 200, message = "Success", response = Topic.class),
            @ApiResponse( code = 400, message = "Error", response = ApiError.class )
        })
        @Path("/{topicId}")
index 5aae1d4..48ee661 100644 (file)
@@ -43,6 +43,7 @@ import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
 import org.onap.dmaap.dbcapi.model.ApiError;
 import org.onap.dmaap.dbcapi.model.Dmaap;
 import org.onap.dmaap.dbcapi.model.MR_Client;
+import org.onap.dmaap.dbcapi.model.ReplicationType;
 import org.onap.dmaap.dbcapi.model.Topic;
 import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
 import org.onap.dmaap.dbcapi.util.DmaapConfig;
@@ -57,7 +58,7 @@ public class DmaapService  extends BaseLoggingClass  {
        
        String topicFactory; // = "org.openecomp.dcae.dmaap.topicFactory";
        String topicMgrRole; // = "org.openecomp.dmaapBC.TopicMgr";
-       String dcaeTopicNs; // = "org.openecomp.dcae.dmaap";
+       
        private boolean multiSite;
        
        
@@ -65,14 +66,14 @@ public class DmaapService  extends BaseLoggingClass  {
                DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
                topicFactory = p.getProperty("MR.TopicFactoryNS", "MR.topicFactoryNS.not.set");
                topicMgrRole = p.getProperty("MR.TopicMgrRole", "MR.TopicMgrRole.not.set" );
-               dcaeTopicNs = dmaapholder.get().getTopicNsRoot();
+
                multiSite = "true".equalsIgnoreCase(p.getProperty("MR.multisite", "true"));
                noEnvironmentPrefix = p.getProperty( "AAF.NoEnvironmentPrefix", "org.onap");
                
                logger.info( "DmaapService settings: " + 
                                " topicFactory=" + topicFactory +
                                " topicMgrRole=" + topicMgrRole +
-                               " dcaeTopicNs=" + dcaeTopicNs +
+                               
                                " multisite=" + multiSite +
                                " noEnvironmentPrefix=" + noEnvironmentPrefix
                                );
@@ -92,7 +93,7 @@ public class DmaapService  extends BaseLoggingClass  {
 
                        nd.setLastMod();
                        dmaapholder.update(nd);
-
+                       
                        AafService aaf = new AafService( ServiceType.AAF_Admin);
                        ApiPolicy apiPolicy = new ApiPolicy();
                        if ( apiPolicy.getUseAuthClass() ) {
@@ -136,6 +137,7 @@ public class DmaapService  extends BaseLoggingClass  {
                if ( ! dmaap.isStatusValid()  || ! nd.getDmaapName().equals(dmaap.getDmaapName()) || dmaap.getVersion().equals( "0") ) {
                        nd.setLastMod();
                        dmaapholder.update(nd);  //need to set this so the following perms will pick up any new vals.
+                       //dcaeTopicNs = dmaapholder.get().getTopicNsRoot();
                        ApiPolicy apiPolicy = new ApiPolicy();
                        if ( apiPolicy.getUseAuthClass()) {
                                ApiPerms p = new ApiPerms();
@@ -166,6 +168,8 @@ public class DmaapService  extends BaseLoggingClass  {
        public String getTopicPerm( String val ) {
                Dmaap dmaap = dmaapholder.get();
                String nsRoot = dmaap.getTopicNsRoot();
+               if ( nsRoot == null ) { return null; }
+               
                String t;
                // in ONAP Casablanca, we assume no distinction of environments reflected in topic namespace
                if ( nsRoot.startsWith(noEnvironmentPrefix) ) {
@@ -190,7 +194,7 @@ public class DmaapService  extends BaseLoggingClass  {
 
        private boolean setTopicMgtPerms( Dmaap nd, AafService aaf ){
                String[] actions = { "create", "destroy" };
-               String instance = ":" + dcaeTopicNs + "." + nd.getDmaapName() + ".mr.topic:" + dcaeTopicNs + "." + nd.getDmaapName();
+               String instance = ":" + nd.getTopicNsRoot() + "." + nd.getDmaapName() + ".mr.topic:" + nd.getTopicNsRoot() + "." + nd.getDmaapName();
                
                for( String action : actions ) {
 
@@ -210,7 +214,7 @@ public class DmaapService  extends BaseLoggingClass  {
                        }
                }
                
-               String t = dcaeTopicNs +"." + nd.getDmaapName() + ".mr.topic";
+               String t = nd.getTopicNsRoot() +"." + nd.getDmaapName() + ".mr.topic";
                String[] s = { "view", "pub", "sub" };
                actions = s;
                instance = "*";
@@ -279,12 +283,14 @@ public class DmaapService  extends BaseLoggingClass  {
                clients.add( nClient );
        
                // initialize Topic
-               Topic mmaTopic = new Topic();
+               Topic mmaTopic = new Topic().init();
                mmaTopic.setTopicName(dmaap.getBridgeAdminTopic());
                mmaTopic.setClients(clients);
                mmaTopic.setOwner("BusController");
                mmaTopic.setTopicDescription("topic reserved for MirrorMaker Administration");
                mmaTopic.setTnxEnabled("false");
+               mmaTopic.setPartitionCount("1");  // a single partition should guarantee message order
+               
                
                ApiError err = new ApiError();
                TopicService svc = new TopicService();
index 5bd62cb..186a003 100644 (file)
@@ -38,6 +38,7 @@ import org.onap.dmaap.dbcapi.aaf.AafService;
 import org.onap.dmaap.dbcapi.aaf.DmaapGrant;
 import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
 import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType;
+import org.onap.dmaap.dbcapi.aaf.AafUserRole;
 import org.onap.dmaap.dbcapi.client.MrProvConnection;
 import org.onap.dmaap.dbcapi.database.DatabaseClass;
 import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
@@ -128,7 +129,22 @@ public class MR_ClientService extends BaseLoggingClass{
                        logger.info( "Client  dcaeLocation that doesn't exist or not specified" );
                        return null;
                }
-               grantClientPerms(  client,  err);
+               // original style: clients specified Role.  This has precedence for backwards
+               //                 compatibility.
+               // ONAP style: clients specify Identity to be assigned to generated Role
+               String role = client.getClientRole();
+               if ( role != null ) {
+                       grantClientRolePerms(  client,  err);
+               } else if ( client.hasClientIdentity() ){
+                       if ( client.isSubscriber() ) {
+                               role = topic.getSubscriberRole();
+                               assignIdentityToRole( client, role, err );
+                       } 
+                       if (client.isPublisher() ) {
+                               role = topic.getPublisherRole();
+                               assignIdentityToRole( client, role, err );
+                       }       
+               } 
                if ( ! client.isStatusValid()) {
                        return null;
                }
@@ -189,26 +205,46 @@ public class MR_ClientService extends BaseLoggingClass{
                return DmaapObject_Status.INVALID;
        }
        
-       private void grantClientPerms( MR_Client client, ApiError err) {
+       private void grantClientRolePerms( MR_Client client, ApiError err) {
                AafService aaf = new AafService(ServiceType.AAF_TopicMgr);
                
                String instance = ":topic." + client.getFqtn();
                client.setStatus( DmaapObject_Status.VALID);
+               String role = client.getClientRole();
                for( String want : client.getAction() ) {
                        int rc;
                        DmaapPerm perm = new DmaapPerm( dmaap.getTopicPerm(), instance, want );
-                       DmaapGrant g = new DmaapGrant( perm, client.getClientRole() );
-                       rc = aaf.addGrant( g );
-                       if ( rc != 201 && rc != 409 ) {
-                               client.setStatus( DmaapObject_Status.INVALID);
-                               err.setCode(rc);
-                               err.setMessage( "Grant of " + dmaap.getTopicPerm() + "|" + instance + "|" + want + " failed for " + client.getClientRole() );
-                               logger.warn( err.getMessage());
-                               return;
-                       } 
+                       if ( role != null ) {
+                               DmaapGrant g = new DmaapGrant( perm, role );
+                               rc = aaf.addGrant( g );
+                               if ( rc != 201 && rc != 409 ) {
+                                       client.setStatus( DmaapObject_Status.INVALID);
+                                       err.setCode(rc);
+                                       err.setMessage( "Grant of " + dmaap.getTopicPerm() + "|" + instance + "|" + want + " failed for " + role );
+                                       logger.warn( err.getMessage());
+                                       return;
+                               } 
+                       } else {
+                               logger.warn( "No Grant of " + dmaap.getTopicPerm() + "|" + instance + "|" + want + " because role is null " );
+                       }
                }
        }
        
+       private void assignIdentityToRole( MR_Client client, String role, ApiError err ) {
+               AafService aaf = new AafService(ServiceType.AAF_TopicMgr);
+
+               AafUserRole ur = new AafUserRole( client.getClientIdentity(), role );
+               int rc = aaf.addUserRole( ur );
+               if ( rc != 201 && rc != 409 ) {
+                       client.setStatus( DmaapObject_Status.INVALID);
+                       err.setCode(rc);
+                       err.setMessage( "Failed to add user " + client.getClientIdentity()+  "  to " + role );
+                       logger.warn( err.getMessage());
+                       return;
+               }
+               client.setStatus( DmaapObject_Status.VALID);
+
+       }
        private void revokeClientPerms( MR_Client client, ApiError err) {
                AafService aaf = new AafService(ServiceType.AAF_TopicMgr);
                
index 56ed967..cfec54e 100644 (file)
@@ -29,7 +29,10 @@ import java.util.Set;
 
 import javax.ws.rs.core.Response.Status;
 
+import org.onap.dmaap.dbcapi.aaf.AafNamespace;
+import org.onap.dmaap.dbcapi.aaf.AafRole;
 import org.onap.dmaap.dbcapi.aaf.AafService;
+import org.onap.dmaap.dbcapi.aaf.DmaapGrant;
 import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType;
 import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
 import org.onap.dmaap.dbcapi.database.DatabaseClass;
@@ -58,7 +61,6 @@ public class TopicService extends BaseLoggingClass {
        private Map<String, Topic> mr_topics = DatabaseClass.getTopics();
        
        private static DmaapService dmaapSvc = new DmaapService();
-       private static Dmaap dmaap = new DmaapService().getDmaap();
        private MR_ClientService clientService = new MR_ClientService();
        private MR_ClusterService clusters = new MR_ClusterService();
        private DcaeLocationService locations = new DcaeLocationService();
@@ -112,6 +114,94 @@ public class TopicService extends BaseLoggingClass {
                apiError.setCode(Status.OK.getStatusCode());
                return t;
        }
+       
+       private void aafTopicSetup(Topic topic, ApiError err ) {
+
+               String t = dmaapSvc.getTopicPerm();
+               if ( t == null ) {
+                       err.setCode(500);
+                       err.setMessage("Unable to establish AAF namespace root: (check /dmaap object)"  );
+                       err.setFields("topicNsRoot");
+                       return;
+               }
+
+               // establish AAF Connection using TopicMgr identity
+               AafService aaf = new AafService(ServiceType.AAF_TopicMgr);
+               
+
+               
+               // create AAF namespace for this topic
+               AafNamespace ns = new AafNamespace( topic.getFqtn(), aaf.getIdentity());
+               {
+                       int rc = aaf.addNamespace( ns );
+                       if ( rc != 201 && rc != 409 ) {
+                               err.setCode(500);
+                               err.setMessage("Unexpected response from AAF:" + rc );
+                               err.setFields("namespace:" + topic.getFqtn() + " identity="+ aaf.getIdentity());
+                               return;
+                       }
+               }
+               
+               // create AAF Roles for MR clients of this topic
+               String rn = "publisher";
+               AafRole pubRole = new AafRole( topic.getFqtn(), rn );
+               int rc = aaf.addRole( pubRole );
+               if ( rc != 201 && rc != 409 ) {
+                       err.setCode(500);
+                       err.setMessage("Unexpected response from AAF:" + rc );
+                       err.setFields("topic:" + topic.getFqtn() + " role="+ rn);
+                       return;
+               }
+               topic.setPublisherRole( pubRole.getFullyQualifiedRole() );
+               
+               rn = "subscriber";
+               AafRole subRole = new AafRole( topic.getFqtn(), rn );
+               rc = aaf.addRole( subRole );
+               if ( rc != 201 && rc != 409 ) {
+                       err.setCode(500);
+                       err.setMessage("Unexpected response from AAF:" + rc );
+                       err.setFields("topic:" + topic.getFqtn() + " role="+ rn);
+                       return;
+               }
+               topic.setSubscriberRole( subRole.getFullyQualifiedRole() );
+       
+               
+               // create AAF perms checked by MR
+               String instance = ":topic." + topic.getFqtn();
+               String[] actions = { "pub", "sub", "view" };
+               for ( String action : actions ){
+                       DmaapPerm perm = new DmaapPerm( t, instance, action );
+                       rc = aaf.addPerm( perm );
+                       if ( rc != 201 && rc != 409 ) {
+                               err.setCode(500);
+                               err.setMessage("Unexpected response from AAF:" + rc );
+                               err.setFields("t="+t + " instance="+ instance + " action="+ action);
+                               return;
+                       }
+                       // Grant perms to our default Roles
+                       if ( action.equals( "pub") || action.equals( "view") ) {
+                               DmaapGrant g = new DmaapGrant( perm, pubRole.getFullyQualifiedRole() );
+                               rc = aaf.addGrant( g );
+                               if ( rc != 201 && rc != 409 ) {
+                                       err.setCode(rc);
+                                       err.setMessage( "Grant of " + perm.toString() + " failed for " + pubRole.getFullyQualifiedRole() );
+                                       logger.warn( err.getMessage());
+                                       return;
+                               } 
+                       }
+                       if ( action.equals( "sub") || action.equals( "view") ) {
+                               DmaapGrant g = new DmaapGrant( perm, subRole.getFullyQualifiedRole() );
+                               rc = aaf.addGrant( g );
+                               if ( rc != 201 && rc != 409 ) {
+                                       err.setCode(rc);
+                                       err.setMessage( "Grant of " + perm.toString() + " failed for " + subRole.getFullyQualifiedRole() );
+                                       logger.warn( err.getMessage());
+                                       return;
+                               } 
+                       }
+
+               }
+       }
 
        public Topic addTopic( Topic topic, ApiError err, Boolean useExisting ) {
                logger.info( "Entry: addTopic");
@@ -135,23 +225,11 @@ public class TopicService extends BaseLoggingClass {
 
                topic.setFqtn( nFqtn );
                
-               AafService aaf = new AafService(ServiceType.AAF_TopicMgr);
-
-               String t = dmaapSvc.getTopicPerm();
-
-               String instance = ":topic." + topic.getFqtn();
+               aafTopicSetup( topic, err );
+               if ( err.getCode() >= 400 ) {
+                       return null;
+               }       
 
-               String[] actions = { "pub", "sub", "view" };
-               for ( String action : actions ){
-                       DmaapPerm perm = new DmaapPerm( t, instance, action );
-                       int rc = aaf.addPerm( perm );
-                       if ( rc != 201 && rc != 409 ) {
-                               err.setCode(500);
-                               err.setMessage("Unexpected response from AAF:" + rc );
-                               err.setFields("t="+t + " instance="+ instance + " action="+ action);
-                               return null;
-                       }
-               }
                if ( topic.getReplicationCase().involvesGlobal() ) {
                        if ( topic.getGlobalMrURL() == null ) {
                                topic.setGlobalMrURL(defaultGlobalMrHost);
@@ -179,6 +257,7 @@ public class TopicService extends BaseLoggingClass {
                                logger.info( "c fqtn=" + c.getFqtn() + " ID=" + c.getMrClientId() + " url=" + c.getTopicURL());
                                MR_Client nc = new MR_Client( c.getDcaeLocationName(), topic.getFqtn(), c.getClientRole(), c.getAction());
                                nc.setFqtn(topic.getFqtn());
+                               nc.setClientIdentity( c.getClientIdentity());
                                logger.info( "nc fqtn=" + nc.getFqtn() + " ID=" + nc.getMrClientId() + " url=" + nc.getTopicURL());
                                clients2.add( clientService.addMr_Client(nc, topic, err));
                                if ( ! err.is2xx()) {
@@ -251,10 +330,11 @@ public class TopicService extends BaseLoggingClass {
                String mmAgentRole = p.getProperty("MM.AgentRole");
                String[] Roles = { mmProvRole, mmAgentRole };
                String[] actions = { "view", "pub", "sub" };
-               Topic bridgeAdminTopic = new Topic();
+               Topic bridgeAdminTopic = new Topic().init();
                bridgeAdminTopic.setTopicName( dmaapSvc.getBridgeAdminFqtn() );
                bridgeAdminTopic.setTopicDescription( "RESERVED topic for MirroMaker Provisioning");
                bridgeAdminTopic.setOwner( "DBC" );
+               
                ArrayList<MR_Client> clients = new ArrayList<MR_Client>();
                for( String role: Roles ) {
                        MR_Client client = new MR_Client();
diff --git a/src/main/resources/schema_10.sql b/src/main/resources/schema_10.sql
new file mode 100644 (file)
index 0000000..715af54
--- /dev/null
@@ -0,0 +1,32 @@
+---
+-- ============LICENSE_START=======================================================
+-- OpenECOMP - org.onap.dbcapi
+-- ================================================================================
+-- Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+-- ================================================================================
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+-- 
+--      http://www.apache.org/licenses/LICENSE-2.0
+-- 
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+-- ============LICENSE_END=========================================================
+---
+
+
+@alter table topic
+       add column      partition_count varchar(10),
+       add column      replication_count varchar(10)
+;
+
+-- set existing topics to have old default values, for display purposes
+update topic
+set partition_count = '2', replication_count = '1';
+
+
+update dmaapbc_sch_ver set version = 10 where version = 9;
diff --git a/src/main/resources/schema_11.sql b/src/main/resources/schema_11.sql
new file mode 100644 (file)
index 0000000..3bf7b1f
--- /dev/null
@@ -0,0 +1,30 @@
+---
+-- ============LICENSE_START=======================================================
+-- OpenECOMP - org.onap.dbcapi
+-- ================================================================================
+-- Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+-- ================================================================================
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+-- 
+--      http://www.apache.org/licenses/LICENSE-2.0
+-- 
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+-- ============LICENSE_END=========================================================
+---
+
+
+@alter table topic
+       add column      publisher_role  varchar(100),
+       add column      subscriber_role varchar(100)
+;
+@alter table mr_client
+       add column      client_identity varchar(100)
+;
+
+update dmaapbc_sch_ver set version = 11 where version = 10;
index 48d1016..917507f 100644 (file)
@@ -21,6 +21,8 @@
 package org.onap.dmaap.dbcapi.resources;
 import org.onap.dmaap.dbcapi.model.*;
 import org.onap.dmaap.dbcapi.service.*;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+
 import static org.junit.Assert.*;
 
 import org.junit.After;
@@ -39,16 +41,44 @@ import javax.ws.rs.Path;
 import javax.ws.rs.GET;
 
 public class DR_SubResourceTest extends JerseyTest{
+       
+       static DmaapObjectFactory factory = new DmaapObjectFactory();
 
        @Override
        protected Application configure() {
                return new ResourceConfig()
                                .register( DR_SubResource.class )
-                               .register( FeedResource.class );
+                               .register( FeedResource.class )
+                               .register( DcaeLocationResource.class )
+                               .register( DmaapResource.class );
        }
 
-       private static final String  fmt = "%24s: %s%n";
        String d, un, up, f, p;
+       
+       @Before
+       public void preTest() throws Exception {
+               try {
+
+                       Dmaap dmaap = factory.genDmaap();
+                       Entity<Dmaap> reqEntity = Entity.entity( dmaap, MediaType.APPLICATION_JSON );
+                       Response resp = target( "dmaap").request().post( reqEntity, Response.class );
+                       System.out.println( resp.getStatus() );
+                       assertTrue( resp.getStatus() == 200 );
+               }catch (Exception e ) {
+               }
+               try {
+                       DcaeLocation loc = factory.genDcaeLocation( "central" );
+                       Entity<DcaeLocation> reqEntity = Entity.entity( loc, MediaType.APPLICATION_JSON );
+                       Response resp = target( "dcaeLocations").request().post( reqEntity, Response.class );
+                       System.out.println( "POST dcaeLocation resp=" + resp.getStatus() + " " + resp.readEntity( String.class ));
+                       if ( resp.getStatus() != 409 ) {
+                               assertTrue( resp.getStatus() == 201 );
+                       }
+               } catch (Exception e ) {
+               }
+       
+
+       }
 /*
        @Before
        public void setUp() throws Exception {
index 931ee28..d500fb6 100644 (file)
@@ -21,15 +21,10 @@ package org.onap.dmaap.dbcapi.testframework;
 
 import static org.junit.Assert.*;
 
-import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
 import static java.lang.System.out;
 import static java.lang.System.err;
 
index d9e3cf7..6623284 100644 (file)
@@ -27,7 +27,7 @@
 
 major=1
 minor=0
-patch=18
+patch=20
 base_version=${major}.${minor}.${patch}
 
 # Release must be completed with git revision # in Jenkins