From e9c94429a8ada3e55854515060df817945b73d87 Mon Sep 17 00:00:00 2001 From: Shwetank Dave Date: Wed, 2 Aug 2017 17:10:38 -0400 Subject: [PATCH] [AAI-26] Adding gizmo data to the repository. Change-Id: I183f837d45acbfe3c673fde1acf8768d5e3fd37b Signed-off-by: Shwetank Dave --- .gitignore | 7 + .gitreview | 4 + License.txt | 21 + README.md | 636 +++++++++++++++++ ajsc-shared-config/README.txt | 8 + ajsc-shared-config/etc/aft.properties | 15 + .../etc/basic-logback_root_logger_level_off.xml | 87 +++ ajsc-shared-config/etc/logback.xml | 212 ++++++ ajsc-shared-config/etc/spm2.jks | Bin 0 -> 62008 bytes antBuild/build.xml | 231 +++++++ bundleconfig-local/README.txt | 2 + bundleconfig-local/RELEASE_NOTES.txt | 2 + .../etc/appprops/AAFUserRoles.properties | 13 + .../appprops/PostProcessorInterceptors.properties | 3 + .../appprops/PreProcessorInterceptors.properties | 4 + .../etc/appprops/app-intercepts.properties | 8 + .../etc/appprops/methodMapper.properties | 30 + .../etc/sysprops/sys-props.properties | 118 ++++ pom.xml | 518 ++++++++++++++ .../crud-api_v1/crud-api/v1/conf/jaxrsBeans.groovy | 11 + .../ajsc/crud-api_v1/crud-api/v1/docs/README.txt | 1 + .../ajsc/crud-api_v1/crud-api/v1/lib/README.txt | 1 + .../crud-api_v1/crud-api/v1/props/module.props | 1 + .../ajsc/crud-api_v1/crud-api/v1/routes/crud.route | 4 + .../crud-api/v1/routes/helloWorld.route | 4 + .../crud-api/v1/routes/jaxrsExample.route | 5 + src/main/assemble/ajsc_module_assembly.xml | 69 ++ src/main/assemble/ajsc_props_assembly.xml | 26 + src/main/assemble/ajsc_runtime_assembly.xml | 47 ++ src/main/bin/start.sh | 54 ++ src/main/config/ajsc-chef.jks | Bin 0 -> 5256 bytes src/main/config/ajsc-jetty.xml | 114 ++++ src/main/config/ajsc-override-web.xml | 53 ++ src/main/config/ajscJetty.jks | Bin 0 -> 3736 bytes src/main/config/cadi.properties | 36 + src/main/config/jul-redirect.properties | 13 + src/main/config/keyfile | 27 + src/main/config/runner-web.xml | 97 +++ src/main/docker/Dockerfile | 26 + src/main/java/org/openecomp/crud/dao/GraphDao.java | 138 ++++ .../org/openecomp/crud/dao/champ/ChampDao.java | 750 +++++++++++++++++++++ src/main/java/org/openecomp/crud/entity/Edge.java | 130 ++++ .../java/org/openecomp/crud/entity/Vertex.java | 102 +++ .../java/org/openecomp/crud/event/GraphEvent.java | 235 +++++++ .../org/openecomp/crud/event/GraphEventEdge.java | 212 ++++++ .../org/openecomp/crud/event/GraphEventVertex.java | 181 +++++ .../openecomp/crud/exception/CrudException.java | 62 ++ .../openecomp/crud/logging/CrudServiceMsgs.java | 128 ++++ .../org/openecomp/crud/logging/LoggingUtil.java | 88 +++ .../openecomp/crud/parser/CrudResponseBuilder.java | 185 +++++ .../crud/service/CrudGraphDataService.java | 209 ++++++ .../openecomp/crud/service/CrudRestService.java | 686 +++++++++++++++++++ .../org/openecomp/crud/service/EdgePayload.java | 115 ++++ .../openecomp/crud/service/JaxrsEchoService.java | 63 ++ .../org/openecomp/crud/service/VertexPayload.java | 117 ++++ .../crud/util/CrudJaxbTransformation.java | 92 +++ .../org/openecomp/crud/util/CrudProperties.java | 77 +++ .../openecomp/crud/util/CrudServiceConstants.java | 53 ++ .../org/openecomp/crud/util/CrudServiceUtil.java | 56 ++ .../java/org/openecomp/crud/util/FileWatcher.java | 48 ++ .../java/org/openecomp/schema/OxmModelLoader.java | 168 +++++ .../org/openecomp/schema/OxmModelValidator.java | 325 +++++++++ .../org/openecomp/schema/RelationshipSchema.java | 138 ++++ .../openecomp/schema/RelationshipSchemaLoader.java | 158 +++++ .../schema/RelationshipSchemaValidator.java | 341 ++++++++++ .../resources/logging/CrudServiceMsgs.properties | 75 +++ ...ame__#__module.ajsc.namespace.version__.context | 1 + src/main/runtime/context/default#0.context | 1 + ...e.name__#__module.ajsc.namespace.version__.json | 1 + src/main/runtime/shiroRole/ajscadmin.json | 1 + ...ontextadmin#__module.ajsc.namespace.name__.json | 1 + .../runtime/shiroRole/contextadmin#default.json | 1 + src/main/runtime/shiroUser/ajsc.json | 1 + src/main/runtime/shiroUserRole/ajsc#ajscadmin.json | 1 + ...ontextadmin#__module.ajsc.namespace.name__.json | 1 + .../shiroUserRole/ajsc#contextadmin#default.json | 1 + .../org/openecomp/crud/dao/champ/ChampDaoTest.java | 624 +++++++++++++++++ .../org/openecomp/schema/OxmModelLoaderTest.java | 22 + .../gremlin/gremlinResponseEmptyVertex.json | 15 + .../gremlin/gremlinResponseMultipleVertex.json | 108 +++ .../resources/gremlin/gremlinResponseVertex.json | 76 +++ version.properties | 13 + 82 files changed, 8308 insertions(+) create mode 100644 .gitignore create mode 100644 .gitreview create mode 100644 License.txt create mode 100644 README.md create mode 100644 ajsc-shared-config/README.txt create mode 100644 ajsc-shared-config/etc/aft.properties create mode 100644 ajsc-shared-config/etc/basic-logback_root_logger_level_off.xml create mode 100644 ajsc-shared-config/etc/logback.xml create mode 100644 ajsc-shared-config/etc/spm2.jks create mode 100644 antBuild/build.xml create mode 100644 bundleconfig-local/README.txt create mode 100644 bundleconfig-local/RELEASE_NOTES.txt create mode 100644 bundleconfig-local/etc/appprops/AAFUserRoles.properties create mode 100644 bundleconfig-local/etc/appprops/PostProcessorInterceptors.properties create mode 100644 bundleconfig-local/etc/appprops/PreProcessorInterceptors.properties create mode 100644 bundleconfig-local/etc/appprops/app-intercepts.properties create mode 100644 bundleconfig-local/etc/appprops/methodMapper.properties create mode 100644 bundleconfig-local/etc/sysprops/sys-props.properties create mode 100644 pom.xml create mode 100644 src/main/ajsc/crud-api_v1/crud-api/v1/conf/jaxrsBeans.groovy create mode 100644 src/main/ajsc/crud-api_v1/crud-api/v1/docs/README.txt create mode 100644 src/main/ajsc/crud-api_v1/crud-api/v1/lib/README.txt create mode 100644 src/main/ajsc/crud-api_v1/crud-api/v1/props/module.props create mode 100644 src/main/ajsc/crud-api_v1/crud-api/v1/routes/crud.route create mode 100644 src/main/ajsc/crud-api_v1/crud-api/v1/routes/helloWorld.route create mode 100644 src/main/ajsc/crud-api_v1/crud-api/v1/routes/jaxrsExample.route create mode 100644 src/main/assemble/ajsc_module_assembly.xml create mode 100644 src/main/assemble/ajsc_props_assembly.xml create mode 100644 src/main/assemble/ajsc_runtime_assembly.xml create mode 100644 src/main/bin/start.sh create mode 100644 src/main/config/ajsc-chef.jks create mode 100644 src/main/config/ajsc-jetty.xml create mode 100644 src/main/config/ajsc-override-web.xml create mode 100644 src/main/config/ajscJetty.jks create mode 100644 src/main/config/cadi.properties create mode 100644 src/main/config/jul-redirect.properties create mode 100644 src/main/config/keyfile create mode 100644 src/main/config/runner-web.xml create mode 100644 src/main/docker/Dockerfile create mode 100644 src/main/java/org/openecomp/crud/dao/GraphDao.java create mode 100644 src/main/java/org/openecomp/crud/dao/champ/ChampDao.java create mode 100644 src/main/java/org/openecomp/crud/entity/Edge.java create mode 100644 src/main/java/org/openecomp/crud/entity/Vertex.java create mode 100644 src/main/java/org/openecomp/crud/event/GraphEvent.java create mode 100644 src/main/java/org/openecomp/crud/event/GraphEventEdge.java create mode 100644 src/main/java/org/openecomp/crud/event/GraphEventVertex.java create mode 100644 src/main/java/org/openecomp/crud/exception/CrudException.java create mode 100644 src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java create mode 100644 src/main/java/org/openecomp/crud/logging/LoggingUtil.java create mode 100644 src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java create mode 100644 src/main/java/org/openecomp/crud/service/CrudGraphDataService.java create mode 100644 src/main/java/org/openecomp/crud/service/CrudRestService.java create mode 100644 src/main/java/org/openecomp/crud/service/EdgePayload.java create mode 100644 src/main/java/org/openecomp/crud/service/JaxrsEchoService.java create mode 100644 src/main/java/org/openecomp/crud/service/VertexPayload.java create mode 100644 src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java create mode 100644 src/main/java/org/openecomp/crud/util/CrudProperties.java create mode 100644 src/main/java/org/openecomp/crud/util/CrudServiceConstants.java create mode 100644 src/main/java/org/openecomp/crud/util/CrudServiceUtil.java create mode 100644 src/main/java/org/openecomp/crud/util/FileWatcher.java create mode 100644 src/main/java/org/openecomp/schema/OxmModelLoader.java create mode 100644 src/main/java/org/openecomp/schema/OxmModelValidator.java create mode 100644 src/main/java/org/openecomp/schema/RelationshipSchema.java create mode 100644 src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java create mode 100644 src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java create mode 100644 src/main/resources/logging/CrudServiceMsgs.properties create mode 100644 src/main/runtime/context/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.context create mode 100644 src/main/runtime/context/default#0.context create mode 100644 src/main/runtime/deploymentPackage/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.json create mode 100644 src/main/runtime/shiroRole/ajscadmin.json create mode 100644 src/main/runtime/shiroRole/contextadmin#__module.ajsc.namespace.name__.json create mode 100644 src/main/runtime/shiroRole/contextadmin#default.json create mode 100644 src/main/runtime/shiroUser/ajsc.json create mode 100644 src/main/runtime/shiroUserRole/ajsc#ajscadmin.json create mode 100644 src/main/runtime/shiroUserRole/ajsc#contextadmin#__module.ajsc.namespace.name__.json create mode 100644 src/main/runtime/shiroUserRole/ajsc#contextadmin#default.json create mode 100644 src/test/java/org/openecomp/crud/dao/champ/ChampDaoTest.java create mode 100644 src/test/java/org/openecomp/schema/OxmModelLoaderTest.java create mode 100644 src/test/resources/gremlin/gremlinResponseEmptyVertex.json create mode 100644 src/test/resources/gremlin/gremlinResponseMultipleVertex.json create mode 100644 src/test/resources/gremlin/gremlinResponseVertex.json create mode 100644 version.properties diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c01c1a --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.classpath +.project +.settings/ +target/ +logs/ +debug-logs/ +.idea/ \ No newline at end of file diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..1a6d347 --- /dev/null +++ b/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=gerrit.onap.org +port=29418 +project=aai/gizmo.git diff --git a/License.txt b/License.txt new file mode 100644 index 0000000..810e147 --- /dev/null +++ b/License.txt @@ -0,0 +1,21 @@ +============LICENSE_START======================================================= +Gizmo +================================================================================ +Copyright © 2017 AT&T Intellectual Property. +Copyright © 2017 Amdocs +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========================================================= + +ECOMP is a trademark and service mark of AT&T Intellectual Property. diff --git a/README.md b/README.md new file mode 100644 index 0000000..adb8bdb --- /dev/null +++ b/README.md @@ -0,0 +1,636 @@ +runsOnPserverrunsOnPserver# CRUD Microservice (Gizmo) + +## Overview +The CRUD microservice implements a set of RESTful APIs which allow a client to perform CREATE, UPDATE, GET, and DELETE operations on verticies and edges within the A&AI graph database. + +## Getting Started + +### Building The Microservice + +After cloning the project, execute the following Maven command from the project's top level directory to build the project: + + > mvn clean install + +Now, you can build your Docker image: + + > docker build -t openecomp/crud-service target + +### Deploying The Microservice + +Push the Docker image that you have built to your Docker repository and pull it down to the location from which you will be running the service. + +**Create the following directories on the host machine:** + + ../logs + ../appconfig + ../appconfig/auth + +You will be mounting these as data volumes when you start the Docker container. + +#### Configuring the Microservice + +Create configuration file **../appconfig/crud-api.properties** + + # List of hostnames/addresses of the graph database + crud.graph.host=graphhost1.onap.com,graphhost2.onap.com + + # Port on which to connect to the graph database + crud.graph.port=2181 + + # Name of the graph on which this service will operate + crud.graph.name=aaigraphautomation + + # Backend storage type for the graph. Types currently supported: + # - cassandra + # - hbase + crud.storage.backend.db=cassandra + + # List of hostnames/addresses of the DMaaP/Kafka cluster on which to post notification events + event.stream.hosts=kafkahost1.onap.com,kafkahost2.onap.com + + # Number of events to bath up before posting to DMaaP/Kafka + event.stream.batch-size=100 + + # Amount of time (in ms) to wait before sending batch of events (when batch does not reach batch-size) + event.stream.batch-timeout=60000 + +Create configuration file **../appconfig/auth/crud-policy.json** + +This policy file defines which client certificates are authorized to use the service's APIs. An example policy file follows: + + { + "roles": [ + { + "name": "admin", + "functions": [ + { + "name": "search", "methods": [ { "name": "GET" },{ "name": "DELETE" }, { "name": "PUT" }, { "name": "POST" } ] + } + ], + "users": [ + { + "username": "CN=admin, OU=My Organization Unit, O=, L=Sometown, ST=SomeProvince, C=CA" + } + ] + } + ] + } + +Create keystore file **../appconfig/auth/tomcat\_keystore** +_tomcat\_keystore_ + +Create a keystore with this name containing whatever CA certificates that you want your instance of the CRUD service to accept for HTTPS traffic. + +#### Start the service + +You can now start the Docker container in the following manner: + + docker run -d \ + -p 9520:9520 \ + -e CONFIG_HOME=/opt/app/crud-service/config/ \ + -e KEY_STORE_PASSWORD={{obfuscated password}} \ + -e KEY_MANAGER_PASSWORD=OBF:{{obfuscated password}} \ + -v //logs:/opt/aai/logroot/AAI-CRUD \ + -v //appconfig:/opt/app/crud-service/config \ + --name crud-service \ + {{your docker repo}}/crud-service + +Where, + + {{your docker repo}} = The Docker repository you have published your CRUD Service image to. + {{obfuscated password}} = The password for your key store/key manager after running it through the Jetty obfuscation tool. + +## API Definitions + +### Echo API + + URL: https://:9520/services/crud-api/v1/echo-service/echo/ + Method: GET + Success Response: 200 + +### Create Vertex + +Vertex payload data is validated against oxm. +* Mandatory attributes are required in payload +* Data type validation is enforced +* Defaults from oxm schema used when not specified in payload + + URL: https://:9520/services/inventory/v8/pserver/ + Method: POST + Body: + { + "properties":{ + "ptnii-equip-name":"e-name", + "equip-type":"server", + "hostname":"myhost", + "equip-vendor":"HP", + "equip-model":"DL380p-nd", + "fqdn":myhost.onap.net", + "purpose":"my-purpose", + "resource-version":"1477013499", + "ipv4-oam-address":"1.2.3.4" + } + } + Success Response: + Code: 201 + Content: + { + "id":"1130672", + "type":"pserver", + "url":"services/inventory/v8/pserver/1130672", + "properties":{ + "ptnii-equip-name":"e-name", + "equip-type":"server", + "hostname":"myhost", + "equip-vendor":"HP", + "equip-model":"DL380p-nd", + "fqdn":myhost.onap.net", + "purpose":"my-purpose", + "resource-version":"1477013499", + "ipv4-oam-address":"1.2.3.4" + } + } + Error Response: + Code: 400 (BAD REQUEST) + Content: Error message describing the bad request failure. + Situation: Invalid Payload or schema error. + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + +Optionally, a vertex can be created by posting to an endpoint which doesn't include the vertex type. + + URL: https://:9520/services/inventory/v8/ + Method: POST + Body: + { + "type":"pserver", + "properties":{ + "ptnii-equip-name":"e-name", + "equip-type":"server", + "hostname":"myhost", + "equip-vendor":"HP", + "equip-model":"DL380p-nd", + "fqdn":myhost.onap.net", + "purpose":"my-purpose", + "resource-version":"1477013499", + "ipv4-oam-address":"1.2.3.4" + } + } + Success Response: + Code: 201 + Content: + { + "id":"1130672", + "type":"pserver", + "url":"services/inventory/v8/pserver/1130672", + "properties":{ + "ptnii-equip-name":"e-name", + "equip-type":"server", + "hostname":"myhost", + "equip-vendor":"HP", + "equip-model":"DL380p-nd", + "fqdn":myhost.onap.net", + "purpose":"my-purpose", + "resource-version":"1477013499", + "ipv4-oam-address":"1.2.3.4" + } + } + Error Response: + Code: 400 (BAD REQUEST) + Content: Error message describing the bad request failure. + Situation: Invalid Payload or schema error. + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + +### Get Vertex + + URL: https://:9520/services/inventory/v8/pserver/ + Method: GET + Success Response: + Code: 200 + Content: + { + "id":"1130672", + "type":"pserver", + "url":"services/inventory/v8/pserver/", + "properties":{ + "ptnii-equip-name":"e-name", + "equip-type":"server", + "hostname":"myhost", + "equip-vendor":"HP", + "equip-model":"DL380p-nd", + "fqdn":myhost.onap.net", + "purpose":"my-purpose", + "resource-version":"1477013499", + "ipv4-oam-address":"1.2.3.4" + }, + "in":[ + ], + "out":[ + { + "id":"1crwnu-6hc-d6vp-oe08g", + "type":"has", + "target":"services/inventory/v8/vserver/40964272", + "url":"services/inventory/relationships/v8/has/1crwnu-6hc-d6vp-oe08g" + } + ] + } + Error Response: + Code: 404 (NOT FOUND) + Situation: Reource Not found + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + +### Get Vertices + + URL: https://:9520/services/inventory/v8/pserver/ + Optional Query Param: ?equip-vendor=HP + Method: GET + Success Response: + Code: 200 + Content: + [ + { + "id":"950296", + "type":"pserver", + "url":"services/inventory/v8/pserver/950296" + }, + { + "id":"1126576", + "type":"pserver", + "url":"services/inventory/v8/pserver/1126576" + }, + { + "id":"1032384", + "type":"pserver", + "url":"services/inventory/v8/pserver/1032384" + } + ] + Error Response: + Code: 404 (NOT FOUND) + Situation: Reource Not found + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + +### Update Vertex + +The PUT command is used to modify an existing vertex. By default, the vertex data is replaced by the content of the payload. However, teh following parameter can be added to the header to perform a PATCH instead of a replace: +**X-HTTP-Method-Override=Patch** + + URL: https://:9520/services/inventory/v8/pserver/ + Method: PUT + Body: Same as POST + Success Response: + Code: 201 + Content: Same as POST + Error Response: + Code: 400 (BAD REQUEST) + Content: Error message describing the bad request failure. + Situation: Invalid Payload or schema error. + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + +### Patch Vertex + + URL: https://:9520/services/inventory/v8/pserver/ + Method: PATCH (Content-Type header set to application/merge-patch+json) + Body: + { + "properties":{ + "ptnii-equip-name":"e-name", + "resource-version":"1477013499", + "ipv4-oam-address":"1.2.3.99" + } + } + Success Response: + Code: 200 + Content: Same as POST + Error Response: + Code: 400 (BAD REQUEST) + Content: Error message describing the bad request failure. + Situation: Invalid Payload or schema error. + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + + ### Delete Vertex + + URL: https://:9520/services/inventory/v8/pserver/ + Method: DELETE + Success Response: + Code: 200 + Error Response: + Code: 404 (NOT FOUND) + Situation: Resource not found + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + +### Create Edge + +When creating an edge, the CRUD service will validate: +* properties match the defined schema +* relationship is valid between the source and target + + URL: https://:9520/services/inventory/relationships/v8/runsOnPserver/ + Method: POST + Body: + { + "source":"services/inventory/v8/vserver/0", + "target":"services/inventory/v8/pserver/7", + "properties":{ + "multiplicity":"many", + "is-parent":true, + "uses-resource":"true", + "has-del-target":"true" + } + } + Success Response: + Code: 201 + Content: + { + "id":"215x5m-6hc-d6vp-oe08g", + "type":"runsOnPserver", + "url":"services/inventory/relationships/v8/has/215x5m-6hc-d6vp-oe08g", + "source":"services/inventory/v8/vserver/8400", + "target":"services/inventory/v8/pserver/40964272", + "properties":{ + "is-parent":"true", + "multiplicity":"many", + "has-del-target":"true", + "uses-resource":"true" + } + } + Error Response: + Code: 400 (BAD REQUEST) + Content: Error message describing the bad request failure. + Situation: Invalid Payload or schema error. + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + +Optionally, an edge can be created by posting to an endpoint which doesn't include the edge type. + + URL: https://:9520/services/inventory/relationships/v8/ + Method: POST + Body: + { + "type":"runsOnPserver", + "source":"services/inventory/v8/vserver/0", + "target":"services/inventory/v8/pserver/7", + "properties":{ + "multiplicity":"many", + "is-parent":true, + "uses-resource":"true", + "has-del-target":"true" + } + } + Success Response: + Code: 201 + Content: Same as above + Error Response: + Code: 400 (BAD REQUEST) + Content: Error message describing the bad request failure. + Situation: Invalid Payload or schema error. + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + +### Get Edge + + URL: https://:9520/services/inventory/relationships/v8/runsOnPserver/ + Method: GET + Success Response: + Code: 200 + Content: + { + "id":"215x5m-6hc-d6vp-oe08g", + "type":"runsOnPserver", + "url":"services/inventory/relationships/v8/has/215x5m-6hc-d6vp-oe08g", + "source":"services/inventory/v8/vserver/8400", + "target":"services/inventory/v8/pserver/40964272", + "properties":{ + "is-parent":"true", + "multiplicity":"many", + "has-del-target":"true", + "uses-resource":"true" + } + } + Error Response: + Code: 404 (NOT FOUND) + Situation: Reource Not found + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + +### Get Edges + + URL: https://:9520/services/inventory/relationships/v8/runsOnPserver + Optional Query Param: ?multiplicity=many + Method: GET + Success Response: + Code: 200 + Content: + [ + { + "id":"1crwnu-6hc-d6vp-oe08g", + "type":"runsOnPserver", + "url":"services/inventory/relationships/v8/runsOnPserver/1crwnu-6hc-d6vp-oe08g", + "source":"services/inventory/v8/vserver/8400", + "target":"services/inventory/v8/pserver/40964272" + }, + { + "id":"215x5m-6hc-d6vp-oe08g", + "type":"runsOnPserver", + "url":"services/inventory/relationships/v8/runsOnPserver/215x5m-6hc-d6vp-oe08g", + "source":"services/inventory/v8/vserver/8400", + "target":"services/inventory/v8/pserver/40964272" + } + ] + Error Response: + Code: 404 (NOT FOUND) + Situation: Reource Not found + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + +### Update Edge + +The PUT command is used to modify an existing edge. By default, the edge data is replaced by the content of the payload. However, the following parameter can be added to the header to perform a PATCH instead of a replace: +**X-HTTP-Method-Override=Patch** + + URL: https://:9520/services/inventory/relationships/v8/runsOnPserver/ + Method: PUT + Body: (**Note that the source and target can not be modified) + { + "properties":{ + "multiplicity":"many", + "is-parent":true, + "uses-resource":"true", + "has-del-target":"true" + } + } + Success Response: + Code: 200 + Content: Same as POST + Error Response: + Code: 400 (BAD REQUEST) + Content: Error message describing the bad request failure. + Situation: Invalid Payload or schema error. + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + +### Patch Edge + + URL: https://:9520/services/inventory/relationships/v8/runsOnPserver/ + Method: PATCH (Content-Type header set to application/merge-patch+json) + Body: + { + "properties":{ + "multiplicity":"many" + } + } + Success Response: + Code: 200 + Content: Same as POST + Error Response: + Code: 400 (BAD REQUEST) + Content: Error message describing the bad request failure. + Situation: Invalid Payload or schema error. + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + +### Delete Edge + + URL: https://:9520/services/inventory/relationships/v8/runsOnPserver/ + Method: DELETE + Success Response: + Code: 200 + Error Response: + Code: 404 (NOT FOUND) + Situation: Resource not found + + Code: 403 (FORBIDDEN) + Content: Error message describing the Authorization failure. + Situation: Authorization failure. + + Code: 415 (UNSUPPORTED MEDIA TYPE) + Situation: Unsupported content type . + + Code: 500 (Internal Server Error) + Content: Error message describing the failure. + Situation: Any scenario not covered by the above error codes. + + + \ No newline at end of file diff --git a/ajsc-shared-config/README.txt b/ajsc-shared-config/README.txt new file mode 100644 index 0000000..e0f7ff1 --- /dev/null +++ b/ajsc-shared-config/README.txt @@ -0,0 +1,8 @@ +#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. + +The ajsc-shared-config folder is included in the service project to provide the functionality of the AJSC_SHARED_CONFIG +location that will exist in CSI envs. This includes the logback.xml for logging configurations, and some csm related +artifacts necessary for proper functionality of the csm framework within the CSI env. Within the 2 profiles that can +be utilized to run the AJSC locally, "runLocal" and "runAjsc", the system propery, "AJSC_SHARED_CONFIG", has been set +to point to this directory. The files in this folder will NOT be copied/moved anywhere within the AJSC SWM package. These +files will already be in existence within the CSI env. \ No newline at end of file diff --git a/ajsc-shared-config/etc/aft.properties b/ajsc-shared-config/etc/aft.properties new file mode 100644 index 0000000..95c7762 --- /dev/null +++ b/ajsc-shared-config/etc/aft.properties @@ -0,0 +1,15 @@ +#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. +# Flow test 319 +# The DEFAULT setup for this file is for deployment to soa cloud node which will use the "bundleconfig/etc/spm2.jks" location +# For Testing Locally, you can set the system property, csiEnable=true, found within bundleconfig-local/etc/sysprops/sys-props.properties +# and switch com.att.aft.keyStore and com.att.aft.trustStore values commented out below to "ajsc-shared-config/etc/spm2.jks" + +#replace proper values for the dummy values. +com.att.aft.discovery.client.environment=TEST +com.att.aft.discovery.client.latitude=35.318900 +com.att.aft.discovery.client.longitude=-80.762200 +com.att.aft.alias=fusionbus +com.att.aft.keyStore=bundleconfig/etc/key.jks +com.att.aft.keyStorePassword=password +com.att.aft.trustStore=bundleconfig/etc/key.jks +com.att.aft.trustStorePassword=password diff --git a/ajsc-shared-config/etc/basic-logback_root_logger_level_off.xml b/ajsc-shared-config/etc/basic-logback_root_logger_level_off.xml new file mode 100644 index 0000000..4ebe2db --- /dev/null +++ b/ajsc-shared-config/etc/basic-logback_root_logger_level_off.xml @@ -0,0 +1,87 @@ + + + + + + ERROR + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{1024} - %msg%n + + + + + + + DEBUG + + ${logDirectory}/info_ajsc.log + + ${logDirectory}/info_ajsc.%i.log.zip + + 1 + 9 + + + 5MB + + + "%d [%thread] %-5level %logger{1024} - %msg%n" + + + + + ERROR + + ${logDirectory}/error_ajsc.log + + ${logDirectory}/error_ajsc.%i.log.zip + + 1 + 9 + + + 5MB + + + + "%d [%thread] %-5level %logger{1024} - %msg%n" + + + + + + INFO + + localhost + USER + + AJSC_AUDIT: [%thread] [%logger] %msg + + + + INFO + + localhost + USER + + AJSC_AUDIT: [%thread] [%logger] mdc:[%mdc] %msg + + + + + + + + + diff --git a/ajsc-shared-config/etc/logback.xml b/ajsc-shared-config/etc/logback.xml new file mode 100644 index 0000000..5972f55 --- /dev/null +++ b/ajsc-shared-config/etc/logback.xml @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + ${errorLogPattern} + + + + + + + + + + + ${logDirectory}/${generalLogName}.log + + ${logDirectory}/${generalLogName}.%d{yyyy-MM-dd}.log.zip + + 60 + + + ${errorLogPattern} + + + + + + INFO + + 256 + + + + + + + + ${logDirectory}/${auditLogName}.log + + ${logDirectory}/${auditLogName}.%d{yyyy-MM-dd}.log.zip + + 60 + + + ${auditMetricPattern} + + + + 256 + + + + + ${logDirectory}/${metricsLogName}.log + + ${logDirectory}/${metricsLogName}.%d{yyyy-MM-dd}.log.zip + + 60 + + + + ${auditMetricPattern} + + + + + + 256 + + + + + ${logDirectory}/${debugLogName}.log + + ${logDirectory}/${debugLogName}.%d{yyyy-MM-dd}.log.zip + + 60 + + + ${errorLogPattern} + + + + + 256 + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ajsc-shared-config/etc/spm2.jks b/ajsc-shared-config/etc/spm2.jks new file mode 100644 index 0000000000000000000000000000000000000000..8ff2a00a105aab80a301cb1e67b567d272d71466 GIT binary patch literal 62008 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundleconfig-local/README.txt b/bundleconfig-local/README.txt new file mode 100644 index 0000000..37f2670 --- /dev/null +++ b/bundleconfig-local/README.txt @@ -0,0 +1,2 @@ +#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. +The bundleconfig-local directory contains the necessary configuration files \ No newline at end of file diff --git a/bundleconfig-local/RELEASE_NOTES.txt b/bundleconfig-local/RELEASE_NOTES.txt new file mode 100644 index 0000000..3cc5590 --- /dev/null +++ b/bundleconfig-local/RELEASE_NOTES.txt @@ -0,0 +1,2 @@ +#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. +Place Release Notes here to provide updated Release information \ No newline at end of file diff --git a/bundleconfig-local/etc/appprops/AAFUserRoles.properties b/bundleconfig-local/etc/appprops/AAFUserRoles.properties new file mode 100644 index 0000000..adb7a10 --- /dev/null +++ b/bundleconfig-local/etc/appprops/AAFUserRoles.properties @@ -0,0 +1,13 @@ +#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. + +#If using AAF for Role based authentication/authorization, define your routes/services which will utilize AAF. The AJSC will +#read this file and protect the routes given with the AAF role defined. + +#The following example would protect the JAXRS echo example service provided with the archetype. +#/services/${namespace}/v1/jaxrs-services/jaxrsExample/echo/*=com.att.ajsc.myper|mymachine|manage + +#The following example would protect ALL AJSC services running within your project. +#/**=com.att.ajsc.myperm|mymachine|manage + +#The following example would protect ALL REST services utilizing the Camel restlet routes. +#/rest/**=com.att.ajsc.myperm|mymachine|manage diff --git a/bundleconfig-local/etc/appprops/PostProcessorInterceptors.properties b/bundleconfig-local/etc/appprops/PostProcessorInterceptors.properties new file mode 100644 index 0000000..08ffefa --- /dev/null +++ b/bundleconfig-local/etc/appprops/PostProcessorInterceptors.properties @@ -0,0 +1,3 @@ +#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. +#This properties file is for defining any PostProcessorInterceptors that have been created for your AJSC service. + diff --git a/bundleconfig-local/etc/appprops/PreProcessorInterceptors.properties b/bundleconfig-local/etc/appprops/PreProcessorInterceptors.properties new file mode 100644 index 0000000..1383071 --- /dev/null +++ b/bundleconfig-local/etc/appprops/PreProcessorInterceptors.properties @@ -0,0 +1,4 @@ +#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. +#This properties file is for defining any PreProcessorInterceptors that have been created for your AJSC service. + +/**=com.att.ajsc.csi.restmethodmap.RestMethodMapInterceptor diff --git a/bundleconfig-local/etc/appprops/app-intercepts.properties b/bundleconfig-local/etc/appprops/app-intercepts.properties new file mode 100644 index 0000000..dada69c --- /dev/null +++ b/bundleconfig-local/etc/appprops/app-intercepts.properties @@ -0,0 +1,8 @@ +#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. + +#This is where all your application intercept strategies must be configured. AJSC reads this property file and adds +#the list of intercepts specified here to the camel context. This can be useful for accessing every exchange object transferred from/to +#each endpoint in the request/response flow and can allow for more precise debugging and/or processing of the exchange. + +#e.g. +#intercepts=org.openecomp.crud.JaxrsEchoService,packagename.class1name,packagename.class2name diff --git a/bundleconfig-local/etc/appprops/methodMapper.properties b/bundleconfig-local/etc/appprops/methodMapper.properties new file mode 100644 index 0000000..bf51749 --- /dev/null +++ b/bundleconfig-local/etc/appprops/methodMapper.properties @@ -0,0 +1,30 @@ +// +//Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. +// Json object holds the method mapping.Update the JSON object with the proper route to logical mapping based +// on the example provided below : +// "helloWorld" = Service Name +// "method" = http method +// "url" = the url component from the route +// "logicalName"= When a combination of method and url from the route matches the json object , +// the logical name is put in the http header as "x-CSI-ServiceName" and "x-CSI-MethodName" +// "dme2url"= if provided it register the endpoint to GRM, it is optional. This is useful for JAX-RS services. + +{ + "helloWorld": [ + { + "method": "get", + "url": "/services/crud-api/v1/echo-service/echo/{input}", + "logicalName": "GetEcho(Logical)", + "dme2url": "/services/crud-api/v1/echo-service/echo/{input}" + } + ], + "errormessage": + [ + { + "method": "get", + "url": "/services/crud-api/v1/errorMessageLookupService2", + "logicalName": "setCAETHeaders(Logical)" + } + + ] +} diff --git a/bundleconfig-local/etc/sysprops/sys-props.properties b/bundleconfig-local/etc/sysprops/sys-props.properties new file mode 100644 index 0000000..b816a44 --- /dev/null +++ b/bundleconfig-local/etc/sysprops/sys-props.properties @@ -0,0 +1,118 @@ +#Copyright (c) 2016 AT&T Intellectual Property. All rights reserved. +#This file is used for defining AJSC system properties for different configuration schemes and is necessary for the AJSC to run properly. +#The sys-props.properties file is used for running locally. The template.sys-props.properties file will be used when deployed +#to a SOA/CSI Cloud node. + +#AJSC System Properties. The following properties are required for ALL AJSC services. If you are adding System Properties for your +#particular service, please add them AFTER all AJSC related System Properties. + +#For Cadi Authorization, use value="authentication-scheme-1 +CadiAuthN=authentication-scheme-1 + +#For Basic Authorization, use value="authentication-scheme-1 +authN=authentication-scheme-2 + +#Persistence used for AJSC meta-data storage. For most environments, "file" should be used. +ajscPersistence=file + +#For Direct Invocation to be enabled (values=true/false) +directInvocationEnable=false + +# If using hawtio for local development, these properties will allow for faster server startup and usage for local development + +hawtio.authenticationEnabled=false +hawtio.config.pullOnStartup=false + +#Removes the extraneous restlet console output +org.restlet.engine.loggerFacadeClass=org.restlet.ext.slf4j.Slf4jLoggerFacade + +#server.host property to be enabled for local DME2 related testing +#server.host= + +#Enable/disable SSL (values=true/false). This property also determines which protocol to use (https if true, http otherwise), to register services into GRM through DME2. +enableSSL=false + + +#Enable/disable EJB Container +ENABLE_EJB=false + +#Enable/disable OSGI +isOSGIEnable=false + +#Generate/Skip api docs +isApiDoc=false + +#CSI related variables for CSM framework +csm.hostname=servername + + +#SOA_CLOUD_ENV is used to register your service with dme2 and can be turned off for local development (values=true/false). +SOA_CLOUD_ENV=false + +#CONTINUE_ON_LISTENER_EXCEPTION will exit the application if there is a DME2 exception at the time of registration. +CONTINUE_ON_LISTENER_EXCEPTION=false + +#Jetty Container ThreadCount Configuration Variables +AJSC_JETTY_ThreadCount_MIN=1 +AJSC_JETTY_ThreadCount_MAX=200 +AJSC_JETTY_IDLETIME_MAX=3000 + +#Camel Context level default threadPool Profile configuration +CAMEL_POOL_SIZE=10 +CAMEL_MAX_POOL_SIZE=20 +CAMEL_KEEP_ALIVE_TIME=60 +CAMEL_MAX_QUEUE_SIZE=1000 + +#GRM/DME2 System Properties +AFT_DME2_CONN_IDLE_TIMEOUTMS=5000 +AJSC_ENV=SOACLOUD + +SOACLOUD_NAMESPACE=com.att.ajsc +SOACLOUD_ENV_CONTEXT=DEV +SOACLOUD_PROTOCOL=http +SOACLOUD_ROUTE_OFFER=DEFAULT + +AFT_LATITUDE=23.4 +AFT_LONGITUDE=33.6 +AFT_ENVIRONMENT=AFTUAT + +#Restlet Component Default Properties +RESTLET_COMPONENT_CONTROLLER_DAEMON=true +RESTLET_COMPONENT_CONTROLLER_SLEEP_TIME_MS=100 +RESTLET_COMPONENT_INBOUND_BUFFER_SIZE=8192 +RESTLET_COMPONENT_MIN_THREADS=1 +RESTLET_COMPONENT_MAX_THREADS=10 +RESTLET_COMPONENT_LOW_THREADS=8 +RESTLET_COMPONENT_MAX_QUEUED=0 +RESTLET_COMPONENT_MAX_CONNECTIONS_PER_HOST=-1 +RESTLET_COMPONENT_MAX_TOTAL_CONNECTIONS=-1 +RESTLET_COMPONENT_OUTBOUND_BUFFER_SIZE=8192 +RESTLET_COMPONENT_PERSISTING_CONNECTIONS=true +RESTLET_COMPONENT_PIPELINING_CONNECTIONS=false +RESTLET_COMPONENT_THREAD_MAX_IDLE_TIME_MS=60000 +RESTLET_COMPONENT_USE_FORWARDED_HEADER=false +RESTLET_COMPONENT_REUSE_ADDRESS=true + +#Externalized jar and properties file location. In CSI environments, there are a few libs that have been externalized to aid +#in CSTEM maintenance of the versions of these libs. The most important to the AJSC is the DME2 lib. Not only is this lib necessary +#for proper registration of your AJSC service on a node, but it is also necessary for running locally as well. Another framework +#used in CSI envs is the CSM framework. These 2 framework libs are shown as "provided" dependencies within the pom.xml. These +#dependencies will be copied into the target/commonLibs folder with the normal "mvn clean package" goal of the AJSC. They will +#then be added to the classpath via AJSC_EXTERNAL_LIB_FOLDERS system property. Any files (mainly property files) that need +#to be on the classpath should be added to the AJSC_EXTERNAL_PROPERTIES_FOLDERS system property. The default scenario when +#testing your AJSC service locally will utilize the target/commonLibs directory for DME2 and CSM related artifacts and 2 +#default csm properties files will be used for local testing with anything CSM knorelated. +#NOTE: we are using maven-replacer-plugin to replace "(doubleUnderscore)basedir(doubleUnderscore)" with ${basedir} within the +#target directory for running locally. Multiple folder locations can be separated by the pipe ("|") character. +#Please, NOTE: for running locally, we are setting this system property in the antBuild/build.xml "runLocal" target and in the +#"runAjsc" profile within the pom.xml. This is to most effectively use maven variables (${basedir}, most specifically. Therefore, +#when running locally, the following 2 properties should be set within the profile(s) themselves. +#Example: target/commonLibs|target/otherLibs +#AJSC_EXTERNAL_LIB_FOLDERS=__basedir__/target/commonLibs +#AJSC_EXTERNAL_PROPERTIES_FOLDERS=__basedir__/ajsc-shared-config/etc +#End of AJSC System Properties + +#Service System Properties. Please, place any Service related System Properties below. +KEY_STORE_PASSWORD=OBF:1i9a1u2a1unz1lr61wn51wn11lss1unz1u301i6o +KEY_MANAGER_PASSWORD=OBF:1i9a1u2a1unz1lr61wn51wn11lss1unz1u301i6o + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..dc0d70c --- /dev/null +++ b/pom.xml @@ -0,0 +1,518 @@ + + 4.0.0 + + + ajsc-archetype-parent + com.att.ajsc + 2.0.0 + + org.onap.aai.gizmo + gizmo + 1.1.0-SNAPSHOT + + + crud-api + v1 + 2.0.0 + /appl/${project.artifactId} + https://nexus.onap.org + + + /appl/${project.artifactId}/${project.version} + ${basedir}/target/swm/package/nix/dist_files${distFilesRoot} + + + 0 + 9520 + + workstation + DEV + google_checks.xml + + java + jacoco + ${project.build.directory}/surefire-reports + + ${project.build.directory}/coverage-reports/jacoco.exec + + false + ${project.version} + + + + + + dom4j + dom4j + 1.6.1 + provided + + + + com.att.aft + dme2 + 3.1.200 + provided + + + commons-io + commons-io + 2.4 + + + + com.google.code.gson + gson + 2.6.2 + + + + com.thinkaurelius.titan + titan-cassandra + 1.0.0 + + + org.apache.tinkerpop + gremlin-groovy + + + org.slf4j + slf4j-log4j12 + + + ch.qos.logback + logback-classic + + + org.apache.tinkerpop + gremlin-core + + + + + + com.thinkaurelius.titan + titan-hbase + 1.0.0 + + + org.apache.tinkerpop + gremlin-groovy + + + org.slf4j + slf4j-log4j12 + + + ch.qos.logback + logback-classic + + + org.apache.tinkerpop + gremlin-core + + + + + + org.apache.hbase + hbase-client + 1.0.2 + + + org.slf4j + slf4j-log4j12 + + + com.google.guava + guava + + + + + + + org.openecomp.aai.logging-service + common-logging + 1.0.0 + + + + ch.qos.logback + logback-core + 1.1.7 + + + + org.onap.aai.aai-common + aai-schema + 1.1.0-SNAPSHOT + + + + org.onap.aai.aai-common + aai-auth + 1.1.0-SNAPSHOT + + + + org.eclipse.persistence + eclipselink + 2.6.2 + + + + + org.openecomp.aai + champ + 1.1.0-SNAPSHOT + compile + + + org.apache.hbase + hbase-client + + + + + + + com.thinkaurelius.titan + titan-cassandra + 1.0.0 + + + org.slf4j + slf4j-log4j12 + + + + + + org.json + json + 20131018 + + + + + + + + central + Maven 2 repository 2 + http://repo2.maven.org/maven2/ + + + ecomp-releases + ECOMP Release Repository + ${nexusproxy}/content/repositories/releases/ + + + ecomp-snapshots + ECOMP Snapshot Repository + ${nexusproxy}/content/repositories/snapshots/ + + + ecomp-staging + ECOMP Staging Repository + ${nexusproxy}/content/repositories/staging/ + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.7 + + + copy-docker-file + package + + copy-resources + + + target + true + + + ${basedir}/src/main/docker + true + + **/* + + + + ${basedir}/src/main/bin/ + + + + + + + + + com.spotify + docker-maven-plugin + 0.4.11 + + true + docker-hub + ${docker.push.registry}/openecomp/${project.artifactId} + ${docker.location} + + latest + + true + + + + + + com.mycila + license-maven-plugin + 3.0 + +

License.txt
+ + src/main/java/** + + + + + + format + + process-sources + + + + + + org.apache.maven.plugins + maven-site-plugin + 3.3 + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.17 + + + + checkstyle + + + + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + org.codehaus.mojo + sonar-maven-plugin + 3.2 + + + + org.jacoco + jacoco-maven-plugin + 0.7.7.201606060606 + + true + + + + jacoco-initialize-unit-tests + + prepare-agent + + + ${project.build.directory}/coverage-reports/jacoco.exec + + + + + + + + + + + + runAjsc + + initialize + + + org.codehaus.mojo + exec-maven-plugin + 1.3.2 + + + initialize + + java + + + false + true + java + com.att.ajsc.runner.Runner + + com.att.ajsc + ajsc-runner + + + + ${basedir}/ajsc-shared-config/etc + + + + + ${runAjscHome} + + + + + + AJSC_HOME + ${runAjscHome} + + + CONFIG_HOME + ${basedir}/appconfig-local/ + + + AJSC_CONF_HOME + ${basedir}/bundleconfig-local + + + logback.configurationFile + ${basedir}/ajsc-shared-config/etc/logback.xml + + + + AJSC_SHARED_CONFIG + ${basedir}/ajsc-shared-config + + + + AJSC_EXTERNAL_LIB_FOLDERS + ${basedir}/target/commonLibs + + + AJSC_EXTERNAL_PROPERTIES_FOLDERS + ${basedir}/ajsc-shared-config/etc + + + + AJSC_SERVICE_NAMESPACE + ${module.ajsc.namespace.name} + + + AJSC_SERVICE_VERSION + ${module.ajsc.namespace.version} + + + SOACLOUD_SERVICE_VERSION + ${project.version} + + + server.port + ${serverPort} + + + + + + context=/ + port=${serverPort} + sslport=${sslport} + + + + + + java + + + + com.att.ajsc + ajsc-runner + ${ajscRuntimeVersion} + + + + + + + + + + + + org.apache.httpcomponents + httpclient + 4.5 + + + org.apache.httpcomponents + httpcore + 4.4.1 + + + org.json + json + 20131018 + + + + + + + ecomp-releases + ECOMP Release Repository + ${nexusproxy}/content/repositories/releases/ + + + ecomp-snapshots + ECOMP Snapshot Repository + ${nexusproxy}/content/repositories/snapshots/ + + + ecomp-javadoc + dav:https://ecomp-nexus:8443/repository/aai/gizmo-javadoc/${project.version} + + + diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/conf/jaxrsBeans.groovy b/src/main/ajsc/crud-api_v1/crud-api/v1/conf/jaxrsBeans.groovy new file mode 100644 index 0000000..8462e3e --- /dev/null +++ b/src/main/ajsc/crud-api_v1/crud-api/v1/conf/jaxrsBeans.groovy @@ -0,0 +1,11 @@ +beans{ + xmlns cxf: "http://camel.apache.org/schema/cxf" + xmlns jaxrs: "http://cxf.apache.org/jaxrs" + xmlns util: "http://www.springframework.org/schema/util" + + echoService(org.openecomp.crud.service.JaxrsEchoService) + + util.list(id: 'jaxrsServices') { + ref(bean:'echoService') + } +} \ No newline at end of file diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/docs/README.txt b/src/main/ajsc/crud-api_v1/crud-api/v1/docs/README.txt new file mode 100644 index 0000000..3707179 --- /dev/null +++ b/src/main/ajsc/crud-api_v1/crud-api/v1/docs/README.txt @@ -0,0 +1 @@ +Place any docs here that you want to access within the ajsc upon deployment of your service. diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/lib/README.txt b/src/main/ajsc/crud-api_v1/crud-api/v1/lib/README.txt new file mode 100644 index 0000000..639e21b --- /dev/null +++ b/src/main/ajsc/crud-api_v1/crud-api/v1/lib/README.txt @@ -0,0 +1 @@ +3rd party JAR's needed by your jars (if any) for a ajsc deployment package go here... \ No newline at end of file diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/props/module.props b/src/main/ajsc/crud-api_v1/crud-api/v1/props/module.props new file mode 100644 index 0000000..17ebc08 --- /dev/null +++ b/src/main/ajsc/crud-api_v1/crud-api/v1/props/module.props @@ -0,0 +1 @@ +EXAMPLE.PROPERTY=EXAMLE_VALUE \ No newline at end of file diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/routes/crud.route b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/crud.route new file mode 100644 index 0000000..185aa6a --- /dev/null +++ b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/crud.route @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/routes/helloWorld.route b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/helloWorld.route new file mode 100644 index 0000000..bc3e178 --- /dev/null +++ b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/helloWorld.route @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/main/ajsc/crud-api_v1/crud-api/v1/routes/jaxrsExample.route b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/jaxrsExample.route new file mode 100644 index 0000000..5158e4f --- /dev/null +++ b/src/main/ajsc/crud-api_v1/crud-api/v1/routes/jaxrsExample.route @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/assemble/ajsc_module_assembly.xml b/src/main/assemble/ajsc_module_assembly.xml new file mode 100644 index 0000000..359f792 --- /dev/null +++ b/src/main/assemble/ajsc_module_assembly.xml @@ -0,0 +1,69 @@ + + + ${version} + false + + zip + + + + ${project.basedir}/target/versioned-ajsc/routes/ + ${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/routes/ + + *.route + + + + + + ${project.basedir}/target/versioned-ajsc/docs/ + ${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/docs/ + + *.* + + + + + + + ${project.basedir}/target/versioned-ajsc/lib/ + ${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/lib/ + + *.jar + + + + + ${project.basedir}/target/versioned-ajsc/extJars/ + ${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/extJars/ + + *.jar + + + + + + ${project.basedir}/target/ + ${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/lib/ + + *.jar + + + + + ${project.basedir}/target/versioned-ajsc/conf/ + ${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/conf/ + + *.* + + + + + + + diff --git a/src/main/assemble/ajsc_props_assembly.xml b/src/main/assemble/ajsc_props_assembly.xml new file mode 100644 index 0000000..6ee4093 --- /dev/null +++ b/src/main/assemble/ajsc_props_assembly.xml @@ -0,0 +1,26 @@ + + + ${version}_properties + false + + zip + + + + ${project.basedir}/target/versioned-ajsc/props + ${module.ajsc.namespace.name}/${module.ajsc.namespace.version}/props/ + + *.props + + + + + + + + diff --git a/src/main/assemble/ajsc_runtime_assembly.xml b/src/main/assemble/ajsc_runtime_assembly.xml new file mode 100644 index 0000000..c86d265 --- /dev/null +++ b/src/main/assemble/ajsc_runtime_assembly.xml @@ -0,0 +1,47 @@ + + + runtimeEnvironment + false + + zip + + + + ${project.basedir}/target/versioned-runtime/context/ + runtime/context/ + + *.context + + + + ${project.basedir}/target/versioned-runtime/serviceProperties/ + runtime/serviceProperties/ + + *.props + + + ${project.basedir}/target/versioned-runtime/shiroRole + runtime/shiroRole/ + + *.json + + + ${project.basedir}/target/versioned-runtime/shiroUser + runtime/shiroUser/ + + *.json + + + ${project.basedir}/target/versioned-runtime/shiroUserRole + runtime/shiroUserRole + + *.json + + + + \ No newline at end of file diff --git a/src/main/bin/start.sh b/src/main/bin/start.sh new file mode 100644 index 0000000..8a68460 --- /dev/null +++ b/src/main/bin/start.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +BASEDIR="/opt/app/crud-api/" +AJSC_HOME="$BASEDIR" +AJSC_CONF_HOME="$AJSC_HOME/bundleconfig/" + +if [ -z "$CONFIG_HOME" ]; then + echo "CONFIG_HOME must be set in order to start up process" + exit 1 +fi + +if [ -z "$KEY_STORE_PASSWORD" ]; then + echo "KEY_STORE_PASSWORD must be set in order to start up process" + exit 1 +else + echo -e "KEY_STORE_PASSWORD=$KEY_STORE_PASSWORD\n" >> $AJSC_CONF_HOME/etc/sysprops/sys-props.properties +fi + +if [ -z "$KEY_MANAGER_PASSWORD" ]; then + echo "KEY_MANAGER_PASSWORD must be set in order to start up process" + exit 1 +else + echo -e "KEY_MANAGER_PASSWORD=$KEY_MANAGER_PASSWORD\n" >> $AJSC_CONF_HOME/etc/sysprops/sys-props.properties +fi + +# Add any spring bean configuration files to the Gizmo deployment +if [ -n "$SERVICE_BEANS" ]; then + echo "Adding the following dynamic service beans to the deployment: " + mkdir -p /tmp/crud-api/v1/conf + for f in `ls $SERVICE_BEANS` + do + cp $SERVICE_BEANS/$f /tmp/crud-api/v1/conf + echo "Adding dynamic service bean $SERVICE_BEANS/$f" + done + jar uf /opt/app/crud-api/services/crud-api_v1.zip* -C /tmp/ crud-api + rm -rf /tmp/crud-api +fi + +CLASSPATH="$AJSC_HOME/lib/*" +CLASSPATH="$CLASSPATH:$AJSC_HOME/extJars/" +CLASSPATH="$CLASSPATH:$AJSC_HOME/etc/" +PROPS="-DAJSC_HOME=$AJSC_HOME" +PROPS="$PROPS -DAJSC_CONF_HOME=$BASEDIR/bundleconfig/" +PROPS="$PROPS -Dlogback.configurationFile=$BASEDIR/bundleconfig/etc/logback.xml" +PROPS="$PROPS -DAJSC_SHARED_CONFIG=$AJSC_CONF_HOME" +PROPS="$PROPS -DAJSC_SERVICE_NAMESPACE=crud-api" +PROPS="$PROPS -DAJSC_SERVICE_VERSION=v1" +PROPS="$PROPS -Dserver.port=9520" +PROPS="$PROPS -DCONFIG_HOME=$CONFIG_HOME" +JVM_MAX_HEAP=${MAX_HEAP:-1024} + +echo $CLASSPATH + +exec java -Xmx${JVM_MAX_HEAP}m $PROPS -classpath $CLASSPATH com.att.ajsc.runner.Runner context=// sslport=9520 diff --git a/src/main/config/ajsc-chef.jks b/src/main/config/ajsc-chef.jks new file mode 100644 index 0000000000000000000000000000000000000000..aeca7700182fb1824b47fca285204122a879ff85 GIT binary patch literal 5256 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 literal 0 HcmV?d00001 diff --git a/src/main/config/ajsc-jetty.xml b/src/main/config/ajsc-jetty.xml new file mode 100644 index 0000000..17814f8 --- /dev/null +++ b/src/main/config/ajsc-jetty.xml @@ -0,0 +1,114 @@ + + + + + + + + true + + + /etc/runner-web.xml + /etc/ajsc-override-web.xml + true + + + false + + + /extJars/json-20131018.jar + + + + + + + + + + + + + + + + + + + + + + + + /extApps + 10 + true + + + + + + + + + file:/auth/tomcat_keystore + + + + + + + + + + + true + true + + + + + + + + + + + + + http/1.1 + + + + + + + + + + + + + + + + + + + + + + 30000 + + + + + + + + + false + + + diff --git a/src/main/config/ajsc-override-web.xml b/src/main/config/ajsc-override-web.xml new file mode 100644 index 0000000..84a7920 --- /dev/null +++ b/src/main/config/ajsc-override-web.xml @@ -0,0 +1,53 @@ + + + + + + InterceptorFilter + /services/* + + + InterceptorFilter + /rest/* + + + + springSecurityFilterChain + /* + + + + ManagementServlet + /mgmt + + + + RestletServlet + /rest/* + + + + CamelServlet + /services/* + + + + jsp + *.jsp + *.jspf + *.jspx + *.xsp + *.JSP + *.JSPF + *.JSPX + *.XSP + + + default + /* + + \ No newline at end of file diff --git a/src/main/config/ajscJetty.jks b/src/main/config/ajscJetty.jks new file mode 100644 index 0000000000000000000000000000000000000000..48cdbff4a8344b9bfa164b340c2fd3b4f7020c41 GIT binary patch literal 3736 zcmcIm`)`xy75+Zo;@EM*>Rd47o-~jPU}JKFn8b+*gxq6897y6^aPAj^NkW*tv>V%4 zyRF*Rc3M>xRjX8OrCU)iVyiZ!iWb^R6&;0+l1^fqRQ0F+3ws`)k1rMKualnhoO9my z^Y^~zyyrcizq$6eZyrKX6az#}iZwQUHo7o6AH_gG9_fKn6tm)nHbLC!7Kg3UqMFnS z!}nCxtZ~`P?CJNqmyXXx;aI2o792JhsgV548_!HZo2o&;_RpUv+ae~{GJf#q z!MdVh8R!xP4nfL{AQh4e5$xF~Iy}(66IPBsk$|-o5>E$Jn_VvLL1hkp0YpcwRi{JyRN4$I;F!}LQV-;fNJSJM_^pU+{Hynlzw2I@?=fy~jM<2ZK zYMg0YqvWj1I5XlGbA?L-Ou3FzW4(#FkNygOh_9TL3HA6+f68Y+hvRAh<;y5IQ>>7;EBd2S36TyJ}*wf9SB8?IRywf@kmB1)ixQLB5S$xjotN$7L)J zY8rA<+=HTYjwS9ruRHay3dFnnV`w8U64wb&uw;1R4}SN?zux3CtbXF@=fJ1(vcPqa z_;C=W8**~&SZ(DbJ;vQh0!~1fkCxM*20JtK!~~P zR{D;@%HsVhs$l7rP4gheg3(ed51PvZU835#YjL;8Y6yLur?!wkF7(58= z{_wy9!|lv~Yb65KL5G*4!sl1`j#<{vynA=Dxxo}@XUgDs8^-y1;PZ>^>`2=oGpxsM z2GiPBzwF|X@l=7rGcn#Ko?H3xV&1d1yKjJX)MhedfLR54xlO^@Vl$I}M!YN@Hoy`F z&O^9;mi{QqOxq>PdI;iy2;O@(8P1lLn{PU7EQ>&diCc!Szq%nhjAEU?{x0i*DFGG_ zBwui@T^nL}jqFcA#luwbKD$dcvm2KAIjA6Hh*Wq+d!vg*dnOIU1+`nh1Ev-nEWshD zi5~U)`}$zaSO!*ZKO2KJEVJOyHqd7mbf1X^_-gG7!Cg->P+uJziVp&?-E4Lh<7T!7 zTE}J!G3iMS#Mh=Cm#6bZ33u}nENn))zae|$7Y0^_fWIzbL1=Zmv8VAl3l40L&%-|CqzOtKIdAVhpm_jooBJElq=JFqjV2 z@v^!dHcKjlJput7HdVFb4^r4;cc)s6$4nNd;aKpYV|5x1`#oFmw+S;T&J=&B!`~J1 zd(!wC3^{C0qdye#IBae^eY?}t66^`+zR_v&9}Tv+^K%?F&px-;Q|GDn)>rR6A%2ag zM)UvA>myLL_`5>KUqGIVnvwjAs5)#0da42jPVN}&GM8>V0n=eCVQmHPzHDbtnq_eS zY;}k4yai^<2~zGq11`A3Is1@?xi+=$Na&vRSReQzet(b+d zr~LDZ!Kf+>uE<^B&O>qLrJJ%%nLH*zBM;=?KfN03=Z$6e5*Ty8t59%9l7*rw+jfN9 zmUJH;wrc^yteUGYfy#ueLwXT!ls`fcz3j_XfRey^6e+2yW?*8^z2Ar=h zvnn&#vUrn^L71LVrlOQ*|PW9frj&V8Ix8l3)CaF^zwDO~%1DUQGTe;RWhYSm*+l zN8zI>VTe_hLe2roM}OkE1N=7kiK%mgZoy#NyZwrcFj+i==)HHg$Ow3=w%0FS8TAIV d$UppJT1E%vmLWa}Z$I0*0!fDe1J|Dh{|_E2uSEa= literal 0 HcmV?d00001 diff --git a/src/main/config/cadi.properties b/src/main/config/cadi.properties new file mode 100644 index 0000000..4720e1c --- /dev/null +++ b/src/main/config/cadi.properties @@ -0,0 +1,36 @@ +#This properties file is used for defining AAF properties related to the CADI framework. This file is used for running AAF framework + +#In order to test functionality of cadi-ajsc-plugin locally cross domain cookie. Cadi "should" find your hostname for you. +#However, we have seen some situations where this fails. A Local testing +#modification can include modifying your hosts file so that you can use "mywebserver.att.com" for your localhost in order +#to test/verify GLO functionality locally. If you are on a Windows machine, you will already have a machine name associated with +#it that will utilize an AT&T domain such as "sbc.com". You may need to add your domain to this as a comma separated list depending +#upon your particular machine domain. This property is commented out as cadi SHOULD find your machine name. With version 1.2.1 of cadi, +#it appears to resolve Mac machine names as well, now. But, this can be somewhat inconsistent depending on your specific working envrironment. +hostname=mywebserver.att.com + +#Setting csp_domain to PROD will allow for testing using your attuid and password through GLO. +csp_domain=PROD +csp_devl_localhost=true + +basic_realm=csp.att.com +#basic_realm=aaf.att.com +basic_warn=TRUE + +cadi_loglevel=WARN +cadi_keyfile=target/swm/package/nix/dist_files/appl/crud-api/etc/keyfile + +# Configure AAF +#These are dummy values add appropriate values required +aaf_url=url + +#AJSC - MECHID +#These are dummy values add appropriate values required +aaf_id=dummyid@ajsc.att.com +aaf_password=enc:277edqJCjT0RlUI3BtbDQa-3Ha-CQGd +aaf_timeout=5000 +aaf_clean_interval=30000 +aaf_user_expires=5000 +aaf_high_count=1000 + + diff --git a/src/main/config/jul-redirect.properties b/src/main/config/jul-redirect.properties new file mode 100644 index 0000000..8b6624d --- /dev/null +++ b/src/main/config/jul-redirect.properties @@ -0,0 +1,13 @@ + +# Bridge JUL->slf4j Logging Configuration File +# +# This file bridges the JUL logging infrastructure into +# SLF4J so JUL logs go to logback implementation provided +# in this project. SLF4J also captures log4j and has +# other framework options as well providing a common +# logging infrastructure for capturing all logs from different +# libraries using different frameworks in one place. + +# Global properties +handlers=org.slf4j.bridge.SLF4JBridgeHandler +.level= ALL diff --git a/src/main/config/keyfile b/src/main/config/keyfile new file mode 100644 index 0000000..882e86a --- /dev/null +++ b/src/main/config/keyfile @@ -0,0 +1,27 @@ +ZuIwp0TkyVPDeX1Up-8JtkMWvjsCpoiu1_VKeWrtrvxunvAke8_tiFyHPPyb2nkhepFYj6tXzpfS +rGz5XF_TH9NbsKaP8u0HV5clz2WriYQRvHS85vjY7hXxkpFuLb7zkLAPqTyIDpj7FiW61NzsRUAq +TM8jH16jr7mBNnb56w24mNGOwznMPcIZKcjgZU1ekaPDFpWyhQElU7Y0q_94P_Gkk45r66Hj22sU +OiOaaftmudZlswLw8-8Zaakqf2yW9HjMVfuYCwSodBHCW5rdB3Ctb5W36rnD_AQco3Ky2PgPmqvk +QkJYuUHpbuDqVHqLOajlKSIGMTIqAIBg51fRaaONtD-Q5xzY8E5wO1YWTLKcP5tsNvUpzM8Wu3NS +ynpGpUcvlTqWWsGzTbzOyamyKkdNdx97sSqjM25Zh1-ps48h6cddGYWpab7SUvqRCS11QBUyLTry +2iwTEHMhHRIbo7PO99ALQfuq9gI1zKGfurJdvLBeBaFs5SCF0AiCZ3WcDO8Rv3HpxVZ2_ShbDxb0 +eMoO6SotXu51fj8Y3-WqsfZziQyEsHyqpg5uQ6yUtz01h5YHLEoVuotF1U4agmQR6kEkYk-wNOiZ +v-8gaA9gtbLoAdKhuKFxQgQLNMf6GzVzZNujbmDzLoZAP_mXAv29aBPaf64Ugzv-Oa5GZdBgD-Xd +_pahML-ionw99r0TnkpShYmDqMKhMdjaP3m87WIAZkIB-L-VTyKcEsJ4340VSzCOsv3waiM0S89u +4cMcG5y-PLY8IoipIlLUPTWD3SjcQ9DV1Dt3T5KjdWLsj48D3W4K4e9PB8yxs0gtUjgVUR2_xEir +G5eDO9Ac1eHFWGDFFP0SgG-TbHJUKlvy9mwLzmU0fC3xPjhqmIr-v0HxF7HN-tmb1LHDorno8tSN +u7kUGcKSchIiFfvkd066crUb2mH7PnXTaWmAjyVj9VsBExFUYEdpHMAV4sAP9-RxZGDRt46UhrDK +QZvvNhBVyOEjHPHWI4vl1r1v8HNH1_2jZu5DVJWyHWR56aCo1lhFH9_X6UAHUHbnXViDONZOVXlT +9-WD0tk2zJGuwrhdZDAnPnAmjfwbwbpnr5Hmex1i1JiD7WVyP1kbfoej2TmdiYbxr9oBYaGQ29JI +aHod7MQCLtvL1z5XgnDPLZ4y3_9SbqHKYbNa8UgZkTLF5EacGThYVFDLA9cbafHDtR1kMGE3vv4D +EJ-0pAYTOGmKlVI7DwNyKsY9JTyudrxTqhOxi9jgcJNWiUaNe9yhL8Pyc2YBqUTTYhh_a2d1rvkZ +0Gh1crviVxqBrIkRKaMRXZ4f1vDLz-3NvG_vwPOo8WRFo5nGmSdTw7CjBaigJ_cYCfDhoP11pEnw +cndsZNcHs-v05LlxeIIMDD_f5Bvz-il_DLA4eK2HqgLdxh8ziSDl2azk14MJY4amzz6reEXUuKLV +RsZGf_jbDGKhE2HuDQ5ovoLOi4OqE1oRuqh-dGxitrYouP2SN1l_1tCEMRth86FMV-6AQtZsvdUo +y9MtQ7e35atjA8nHtgADlDTmJBKQiUHUsOZ77p1qp17HAFMovUkc739opfEYnKUn6Itpw5Ipm_Is +ra6chJUfMpOFof5rb5OjqFAN27c_-mPo1lQU3ndYlKGh_n5V8ufX6v2Yri8WzOPf6hjVYotkmoMP +NPAICDCB8W5ddBjsopzLVVEtaXDu9Qj6-zf77hT4iQ7rBd2Ner8iLqN3Kis0dvkNM3_uH8onau1G +Y_YYw7PPSZyd2S_7Dd6G-IG4ayO6e5DD6oUwwekyiQI_3rTXNa_wldGxqW9u818010ekE4Qdlfcj +beIn7fAeaOjReZ87hRgWyMs-EgTVHw8RL3yI_O6VvRTVRONRF1Y4C_-IYa8z-bfrwXx3BBd9TTgb +EnS9wVOyC2OgUN6BhPLGLhxzkJ05nEjizXEc9t5EPYoSRwesajGGrrG_0-qWbuU5hKLPLkyeJLHb +5HXOTVsrUR59Vov2M3_EswkxcImblox3k3VS2yihZMGyfqLzZIUXgd8ufkevKKU6DxwacGTb \ No newline at end of file diff --git a/src/main/config/runner-web.xml b/src/main/config/runner-web.xml new file mode 100644 index 0000000..b51aff4 --- /dev/null +++ b/src/main/config/runner-web.xml @@ -0,0 +1,97 @@ + + + + + + contextConfigLocation + /WEB-INF/spring-servlet.xml, + classpath:applicationContext.xml + + + + + spring.profiles.default + nooauth + + + + org.springframework.web.context.ContextLoaderListener + + + + ManagementServlet + ajsc.ManagementServlet + + + + + InterceptorFilter + ajsc.filters.InterceptorFilter + + preProcessor_interceptor_config_file + /etc/PreProcessorInterceptors.properties + + + postProcessor_interceptor_config_file + /etc/PostProcessorInterceptors.properties + + + + + + RestletServlet + ajsc.restlet.RestletSpringServlet + + org.restlet.component + restletComponent + + + + + CamelServlet + ajsc.servlet.AjscCamelServlet + + + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + + spring + org.springframework.web.servlet.DispatcherServlet + 1 + + + + + + + + jsp + org.apache.jasper.servlet.JspServlet + + + + + + + + + default + org.eclipse.jetty.servlet.DefaultServlet + + dirAllowed + true + + + + diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile new file mode 100644 index 0000000..e4459d1 --- /dev/null +++ b/src/main/docker/Dockerfile @@ -0,0 +1,26 @@ +FROM ubuntu:14.04 + +ARG MICRO_HOME=/opt/app/crud-api +ARG BIN_HOME=$MICRO_HOME/bin + +RUN apt-get update + +# Install and setup java8 +RUN apt-get update && apt-get install -y software-properties-common +## sudo -E is required to preserve the environment. If you remove that line, it will most like freeze at this step +RUN sudo -E add-apt-repository ppa:openjdk-r/ppa && apt-get update && apt-get install -y openjdk-8-jdk +## Setup JAVA_HOME, this is useful for docker commandline +ENV JAVA_HOME usr/lib/jvm/java-8-openjdk-amd64 +RUN export JAVA_HOME + +# Build up the deployment folder structure +RUN mkdir -p $MICRO_HOME +ADD swm/package/nix/dist_files/appl/crud-api/* $MICRO_HOME/ +RUN mkdir -p $BIN_HOME +COPY *.sh $BIN_HOME +RUN chmod 755 $BIN_HOME/* +RUN ln -s /logs $MICRO_HOME/logs + +EXPOSE 9520 9520 + +CMD ["/opt/app/crud-api/bin/start.sh"] diff --git a/src/main/java/org/openecomp/crud/dao/GraphDao.java b/src/main/java/org/openecomp/crud/dao/GraphDao.java new file mode 100644 index 0000000..20b568c --- /dev/null +++ b/src/main/java/org/openecomp/crud/dao/GraphDao.java @@ -0,0 +1,138 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.dao; + +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; + +import java.util.List; +import java.util.Map; + +public interface GraphDao { + + public Vertex getVertex(String id, String type) throws CrudException; + + /** + * Retrieve all of the edges which are incident to the vertex with the specified identifier. + * + * @param id - The unique identifier of the vertex to retrieve the edges for. + * @return - A collection of edges. + * @throws CrudException + */ + public List getVertexEdges(String id) throws CrudException; + + /** + * Retrieve a collection of {@link Vertex} objects which match the supplied type label + * and filter properties. + * + * @param type - The vertex type that we want to retrieve. + * @param filter - The parameters to filter our results by. + * @return - A collection of vertices. + * @throws CrudException + */ + public List getVertices(String type, Map filter) throws CrudException; + + /** + * Retrieve an {@link Edge} from the graph database by specifying its unique identifier. + * + * @param id - The unique identifier for the Edge to be retrieved. + * @return - The Edge corresponding to the specified identifier. + * @throws CrudException + */ + public Edge getEdge(String id, String type) throws CrudException; + + /** + * Retrieve a collection of {@link Edge} objects with a given type and which match a set of + * supplied filter parameters. + * + * @param type - The type of edges that we are interested in. + * @param filter - The parameters that we want to filter our edges by. + * @return - A collection of edges which match the supplied filter parameters. + * @throws CrudException + */ + public List getEdges(String type, Map filter) throws CrudException; + + /** + * Insert a new {@link Vertex} into the graph data store. + * + * @param type - The type label to assign to the vertex. + * @param properties - The properties to associated with this vertex. + * @return - The {@link Vertex} object that was created. + * @throws CrudException + */ + public Vertex addVertex(String type, Map properties) throws CrudException; + + /** + * Updates an existing {@link Vertex}. + * + * @param id - The unique identifier of the vertex to be updated. + * @param properties - The properties to associate with the vertex. + * @return - The udpated vertex. + * @throws CrudException + */ + public Vertex updateVertex(String id, String type, Map properties) + throws CrudException; + + /** + * Removes the specified vertex from the graph data base. + * + *

NOTE: The vertex MUST contain NO incident edges before it can be deleted. + * + * @param id - The unique identifier of the vertex to be deleted. + * @throws CrudException + */ + public void deleteVertex(String id, String type) throws CrudException; + + /** + * Adds an edge to the graph database. + * + * @param type - The 'type' label to apply to the edge. + * @param source - The source vertex for this edge. + * @param target - The target vertex for this edge. + * @param properties - The properties map to associate with this edge. + * @return - The {@link Edge} object that was created. + * @throws CrudException + */ + public Edge addEdge(String type, Vertex source, Vertex target, Map properties) + throws CrudException; + + /** + * Updates an existing {@link Edge}. + * + * @param id - The unique identifier of the edge to be updated. + * @param properties - The properties to associate with the edge. + * @return - The update edge. + * @throws CrudException + */ + public Edge updateEdge(Edge edge) throws CrudException; + + /** + * Remove the specified edge from the graph data base. + * + * @param id - The unique identifier of the edge to be deleted. + * @throws CrudException + */ + public void deleteEdge(String id, String type) throws CrudException; +} diff --git a/src/main/java/org/openecomp/crud/dao/champ/ChampDao.java b/src/main/java/org/openecomp/crud/dao/champ/ChampDao.java new file mode 100644 index 0000000..ff0a332 --- /dev/null +++ b/src/main/java/org/openecomp/crud/dao/champ/ChampDao.java @@ -0,0 +1,750 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.dao.champ; + +import org.openecomp.aai.champ.ChampAPI; +import org.openecomp.aai.champ.ChampGraph; +import org.openecomp.aai.champ.exceptions.ChampMarshallingException; +import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException; +import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException; +import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException; +import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException; +import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl; +import org.openecomp.aai.champ.model.ChampObject; +import org.openecomp.aai.champ.model.ChampRelationship; +import org.openecomp.aai.champ.model.fluent.object.ObjectBuildOrPropertiesStep; +import org.openecomp.cl.api.Logger; +import org.openecomp.cl.eelf.LoggerFactory; +import org.openecomp.crud.dao.GraphDao; +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.logging.CrudServiceMsgs; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +/** + * This is the integration layer between the CRUD API service and the low level Champ library + * for graph database interaction. + */ +public class ChampDao implements GraphDao { + + public static final String CONFIG_STORAGE_BACKEND = "storage.backend"; + public static final String CONFIG_STORAGE_BACKEND_DB = "storage.backend.db"; + public static final String STORAGE_HBASE_DB = "hbase"; + public static final String STORAGE_CASSANDRA_DB = "cassandra"; + public static final String CONFIG_STORAGE_HOSTNAMES = "storage.hostnames"; + public static final String CONFIG_STORAGE_PORT = "storage.port"; + public static final String CONFIG_HBASE_ZNODE_PARENT = "storage.hbase.ext.zookeeper.znode.parent"; + public static final String CONFIG_GRAPH_NAME = "graph.name"; + public static final String GRAPH_UNQ_INSTANCE_ID_SUFFIX = "graph.unique-instance-id-suffix"; + + public static final String CONFIG_EVENT_STREAM_PUBLISHER = "event.stream.publisher"; + public static final String CONFIG_EVENT_STREAM_NUM_PUBLISHERS = "event.stream.num-publishers"; + + + public static final String DEFAULT_GRAPH_NAME = "default_graph"; + + /** + * Set of configuration properties for the DAI. + */ + private Properties daoConfig; + + /** + * Instance of the API used for interacting with the Champ library. + */ + private ChampGraph champApi = null; + + private Logger logger = LoggerFactory.getInstance().getLogger(ChampDao.class.getName()); + + + /** + * Creates a new instance of the ChampDao. + * + * @param config - Set of configuration properties to be applied to this instance + * of the DAO. + */ + public ChampDao(Properties config) { + + // Store the configuration properties. + daoConfig = config; + + // Apply the configuration to the DAO. + configure(); + + } + + + @Override + public Vertex getVertex(String id, String type) throws CrudException { + + try { + + if (logger.isDebugEnabled()) { + logger.debug("getVertex with id: " + id); + } + + long idAsLong = Long.parseLong(id); + + // Request the vertex from the graph db. + Optional retrievedVertex = champApi.retrieveObject(idAsLong); + + // Did we find it? + if (retrievedVertex.isPresent() && retrievedVertex.get().getProperties() + .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName())!=null && retrievedVertex.get().getProperties() + .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString() + .equalsIgnoreCase(type)) { + + // Yup, convert it to a Vector object and return it. + return vertexFromChampObject(retrievedVertex.get(),type); + + } else { + + // We didn't find a vertex with the supplied id, so just throw an + // exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + } catch (ChampUnmarshallingException e) { + + // Something went wrong - throw an exception. + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + + @Override + public List getVertexEdges(String id) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("get Edges incident to vertex with id: " + id + " from graph"); + } + + try { + long idAsLong = Long.parseLong(id); // GDF - what to do about id??? + + // Request the vertex from the graph db. + Optional retrievedVertex = champApi.retrieveObject(idAsLong); + + // Did we find it? + if (retrievedVertex.isPresent()) { + + // Query the Champ library for the edges which are incident to the specified + // vertex. + Stream relationships = + champApi.retrieveRelationships(retrievedVertex.get()); + + // Build an edge list from the result stream. + List edges = new ArrayList(); + relationships.forEach(r -> edges.add(edgeFromChampRelationship(r))); + + return edges; + + } else { + + // We couldn't find the specified vertex, so throw an exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + } catch (ChampUnmarshallingException e) { + + // Something went wrong, so throw an exception. + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + + } catch (ChampObjectNotExistsException e) { + + // We couldn't find the specified vertex, so throw an exception. + throw new CrudException("No vertex with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + } + + + @Override + public Vertex addVertex(String type, Map properties) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Add/update vertex: {label: " + type + + " properties:" + propertiesMapToString(properties)); + } + + //Add the aai_node_type so that AAI can read the data created by gizmo + properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + // Create an object to represent our vertex in the format expected by the Champ library. + ChampObject objectToCreate = buildChampObject(type, properties); + + try { + + // Ask the Champ library to store our vertex, placing the returned object into a + // list so that we can easily put that into our result object. + return vertexFromChampObject(champApi.storeObject(objectToCreate),type); + + } catch (ChampMarshallingException + | ChampSchemaViolationException + | ChampObjectNotExistsException e) { + + // Something went wrong - throw an exception. + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + + @Override + public Vertex updateVertex(String id, String type, Map properties) + throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Update vertex with id: " + id + " with properties: " + + propertiesMapToString(properties)); + } + //Add the aai_node_type so that AAI can read the data created by gizmo + properties.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + try { + // Now, build the updated version of the Champ Object... + ChampObject updateObject = buildChampObject(id, type, properties); + // ...and send it to the Champ library. + return vertexFromChampObject(champApi.replaceObject(updateObject),type); + + } catch (ChampObjectNotExistsException e) { + throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND); + } catch (NumberFormatException | ChampMarshallingException | ChampSchemaViolationException e) { + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + + } + + + @Override + public List getVertices(String type, Map filter) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Retrieve vertices with type label: " + type + " which map query parameters: " + + propertiesMapToString(filter)); + } + + + filter.put(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName(), type); + + + Stream retrievedVertices = champApi.queryObjects(filter); + + List vertices = retrievedVertices + .map(v -> vertexFromChampObject(v,type)) + .collect(Collectors.toList()); + + + if (logger.isDebugEnabled()) { + logger.debug("Resulting vertex list: " + retrievedVertices); + } + + // ...and return it to the caller. + return vertices; + } + + private Object getRelKey(String id) { + Object key = id; + // convert into Long if applicable . TODO : revisit in story NUC-304 + try { + key = Long.parseLong(id); + } catch (NumberFormatException e) { + // The id isn't a Long, leave it as a string + } + + return key; + } + + @Override + public Edge getEdge(String id, String type) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Get edge with id: " + id); + } + + try { + + // Request the edge from the graph db. + Optional relationship = champApi.retrieveRelationship(getRelKey(id)); + + // Did we find it? + if (relationship.isPresent() && relationship.get().getType().equals(type)) { + + // Yup - return the result. + return edgeFromChampRelationship(relationship.get()); + + } else { + + // We didn't find an edge with the supplied id, so throw an exception. + throw new CrudException("No edge with id " + id + " found in graph", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + } catch (ChampUnmarshallingException e) { + + // Something went wrong, so throw an exception. + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public Edge addEdge(String type, + Vertex source, + Vertex target, + Map properties) throws CrudException { + + // For now, assume source and target are straight ids... + try { + + Optional sourceObject + = champApi.retrieveObject(Long.parseLong(source.getId().get())); + if (!sourceObject.isPresent() || !sourceObject.get().getType().equals(source.getType())) { + throw new CrudException("Error creating edge - source vertex with id " + source + + " does not exist in graph data base", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + Optional targetObject + = champApi.retrieveObject(Long.parseLong(target.getId().get())); + if (!targetObject.isPresent() || !targetObject.get().getType().equals(target.getType())) { + throw new CrudException("Error creating edge - target vertex with id " + target + + " does not exist in graph data base", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + // Now, create the ChampRelationship object for our edge and store it in + // the graph database. + return edgeFromChampRelationship( + champApi.storeRelationship( + new ChampRelationship.Builder(sourceObject.get(), targetObject.get(), type) + .properties(properties) + .build())); + + } catch (ChampMarshallingException + | ChampObjectNotExistsException + | ChampSchemaViolationException + | ChampRelationshipNotExistsException + | ChampUnmarshallingException e) { + + throw new CrudException("Error creating edge: " + e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + + @Override + public List getEdges(String type, Map filter) throws CrudException { + + filter.put(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), type); + + Stream retrievedRelationships = champApi.queryRelationships(filter); + // Process the result stream from the Champ library into an Edge list, keeping only + // edges of the specified type. + List edges = retrievedRelationships + .map(r -> edgeFromChampRelationship(r)) + .collect(Collectors.toList()); + + return edges; + } + + @Override + public Edge updateEdge(Edge edge) throws CrudException { + + if (logger.isDebugEnabled()) { + logger.debug("Update edge with id: " + edge.getId() + " with properties: " + + propertiesMapToString(edge.getProperties())); + } + + try { + // Now, build the updated version of the Champ Relationship... + ChampRelationship updateRelationship = new ChampRelationship.Builder( + buildChampObject(edge.getSource().getId().get(), edge.getSource().getType(), + edge.getSource().getProperties()), + buildChampObject(edge.getTarget().getId().get(), edge.getTarget().getType(), + edge.getTarget().getProperties()), + edge.getType()).key(getRelKey(edge.getId().get())) + .properties(edge.getProperties()).build(); + // ...and send it to the Champ library. + return edgeFromChampRelationship(champApi.replaceRelationship(updateRelationship)); + + + } catch (ChampRelationshipNotExistsException ex) { + throw new CrudException("Not Found", javax.ws.rs.core.Response.Status.NOT_FOUND); + } catch (NumberFormatException | ChampUnmarshallingException | ChampMarshallingException + | ChampSchemaViolationException ex) { + + throw new CrudException(ex.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public void deleteVertex(String id, String type) throws CrudException { + + try { + + // First, retrieve the vertex that we intend to delete. + Optional retrievedVertex = champApi.retrieveObject(Long.parseLong(id)); + + // Did we find it? + if (!retrievedVertex.isPresent() || !retrievedVertex.get().getType().equals(type)) { + throw new CrudException("Failed to delete vertex with id: " + + id + " - vertex does not exist.", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + // Now, verify that there are no edges incident to the vertex (they must be deleted + // first if so). + Stream relationships = + champApi.retrieveRelationships(retrievedVertex.get()); + + if (relationships.count() > 0) { + throw new CrudException("Attempt to delete vertex with id " + + id + " which has incident edges.", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + // Finally, we can attempt to delete our vertex. + champApi.deleteObject(Long.parseLong(id)); + + + } catch (NumberFormatException + | ChampUnmarshallingException + | ChampObjectNotExistsException e) { + + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + @Override + public void deleteEdge(String id, String type) throws CrudException { + + try { + + // First, retrieve the edge that we want to delete. + Optional relationshipToDelete + = champApi.retrieveRelationship(getRelKey(id)); + + + // Did we find it? + if (!relationshipToDelete.isPresent() || !relationshipToDelete.get().getType().equals(type)) { + throw new CrudException("Failed to delete edge with id: " + id + " - edge does not exist", + javax.ws.rs.core.Response.Status.NOT_FOUND); + } + + // Now we can delete the edge. + champApi.deleteRelationship(relationshipToDelete.get()); + + } catch (ChampRelationshipNotExistsException + | NumberFormatException + | ChampUnmarshallingException e) { + + throw new CrudException(e.getMessage(), + javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR); + } + } + + + /** + * This helper method generates a string representation of a properties map for + * logging purposes. + * + * @param properties - The properties map to be converted. + * @return - The log statement friendly conversion of the properties map. + */ + private String propertiesMapToString(Map properties) { + + StringBuilder sb = new StringBuilder(); + sb.append("{"); + + for (String key : properties.keySet()) { + sb.append("(").append(key).append(" -> ").append(properties.get(key)).append(") "); + } + + sb.append("}"); + + return sb.toString(); + } + + + /** + * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library. + * + * @param type - The type to assign to our ChampObject + * @param properties - The set of properties to assign to our ChampObject + * @return - A populated ChampObject + */ + private ChampObject buildChampObject(String type, Map properties) { + + ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create() + .ofType(type) + .withoutKey(); + + for (String key : properties.keySet()) { + objectInProgress.withProperty(key, properties.get(key)); + } + return objectInProgress.build(); + } + + + /** + * This helper method constructs a {@link ChampObject} suitable for passing to the Champ library. + * + * @param id - Unique identifier for this object. + * @param type - The type to assign to our ChampObject + * @param properties - The set of properties to assign to our ChampObject + * @return - A populated ChampObject + */ + private ChampObject buildChampObject(String id, String type, Map properties) { + + ObjectBuildOrPropertiesStep objectInProgress = ChampObject.create() + .ofType(type) + .withKey(Long.parseLong(id)); + + for (String key : properties.keySet()) { + objectInProgress.withProperty(key, properties.get(key)); + } + return objectInProgress.build(); + } + + + + + private Vertex vertexFromChampObject(ChampObject champObject, String type) { + + // Get the identifier for this vertex from the Champ object. + Object id = champObject.getKey().orElse(""); + + // Start building our {@link Vertex} object. + Vertex.Builder vertexBuilder = new Vertex.Builder(type); + vertexBuilder.id(id.toString()); + + // Convert the properties associated with the Champ object into the form expected for + // a Vertex object. + for (String key : champObject.getProperties().keySet()) { + vertexBuilder.property(key, champObject.getProperties().get(key)); + } + + // ...and return it. + return vertexBuilder.build(); + } + + + /** + * This helper method converts a {@link ChampRelationship} from the Champ library into an + * equivalent {@link Edge} object that is understood by the CRUD Service. + * + * @param relationship - The ChampRelationship object to be converted. + * @return - An Edge object corresponding to the supplied ChampRelationship + */ + private Edge edgeFromChampRelationship(ChampRelationship relationship) { + + // Populate the edge's id, if available. + Object relationshipId = relationship.getKey().orElse(""); + + Edge.Builder edgeBuilder = new Edge.Builder(relationship.getType()) + .id(relationshipId.toString()); + edgeBuilder.source(vertexFromChampObject(relationship.getSource(), + relationship.getSource().getProperties() + .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null + ? relationship.getSource().getType() + : relationship.getSource().getProperties() + .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString())); + edgeBuilder.target(vertexFromChampObject(relationship.getTarget(), + relationship.getTarget().getProperties() + .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()) == null + ? relationship.getTarget().getType() + : relationship.getTarget().getProperties() + .get(org.openecomp.schema.OxmModelValidator.Metadata.NODE_TYPE.propertyName()).toString())); + + for (String key : relationship.getProperties().keySet()) { + edgeBuilder.property(key, relationship.getProperties().get(key).toString()); + } + + return edgeBuilder.build(); + } + + + /** + * Performs all one-time configuration operations which are required when creating + * a new instance of the DAO. + */ + private void configure() { + + // Instantiate the Champ library API. + try { + + // Determine which back end we are using. + switch (getBackendTypeFromConfig()) { + + case IN_MEMORY: + + logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO, + "In Memory", + daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME), + "Not applicable"); + + champApi = ChampGraph.Factory.newInstance(ChampGraph.Type.IN_MEMORY, + daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME)); + + break; + + case TITAN: + try { + String db = daoConfig.getProperty(CONFIG_STORAGE_BACKEND_DB); + Short graphIdSuffix = (short) new Random().nextInt(Short.MAX_VALUE); + logger.info(CrudServiceMsgs.TITAN_GRAPH_INFO, GRAPH_UNQ_INSTANCE_ID_SUFFIX + + ": = " + graphIdSuffix); + if (db.equalsIgnoreCase(STORAGE_CASSANDRA_DB)) { + logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO, "Titan with cassandra backend", + daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME), + daoConfig.getProperty(CONFIG_STORAGE_HOSTNAMES)); + + TitanChampGraphImpl.Builder champApiBuilder = + new TitanChampGraphImpl.Builder(daoConfig.getProperty(CONFIG_GRAPH_NAME, + DEFAULT_GRAPH_NAME)) + .property("storage.backend", "cassandrathrift") + .property(GRAPH_UNQ_INSTANCE_ID_SUFFIX, graphIdSuffix) + .property("storage.hostname", daoConfig.get(CONFIG_STORAGE_HOSTNAMES)); + + if (daoConfig.containsKey(CONFIG_EVENT_STREAM_PUBLISHER)) { + champApiBuilder.property("champ.event.stream.publisher", + daoConfig.get(CONFIG_EVENT_STREAM_PUBLISHER)); + } + + if (daoConfig.containsKey(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)) { + champApiBuilder.property("champ.event.stream.publisher-pool-size", + daoConfig.get(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)); + } + + champApi = champApiBuilder.build(); + + } else if (db.equalsIgnoreCase(STORAGE_HBASE_DB)) { + + logger.info(CrudServiceMsgs.INSTANTIATE_GRAPH_DAO, "Titan with Hbase backend", + daoConfig.getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME), + daoConfig.getProperty(CONFIG_STORAGE_HOSTNAMES)); + TitanChampGraphImpl.Builder champApiBuilder = + new TitanChampGraphImpl.Builder(daoConfig + .getProperty(CONFIG_GRAPH_NAME, DEFAULT_GRAPH_NAME)) + .property("storage.backend", "hbase") + .property("storage.hbase.ext.zookeeper.znode.parent", + daoConfig.get(CONFIG_HBASE_ZNODE_PARENT)) + .property("storage.port", daoConfig.get(CONFIG_STORAGE_PORT)) + .property(GRAPH_UNQ_INSTANCE_ID_SUFFIX, graphIdSuffix) + .property("storage.hostname", daoConfig.get(CONFIG_STORAGE_HOSTNAMES)); + + if (daoConfig.containsKey(CONFIG_EVENT_STREAM_PUBLISHER)) { + champApiBuilder.property("champ.event.stream.publisher", + daoConfig.get(CONFIG_EVENT_STREAM_PUBLISHER)); + } + + if (daoConfig.containsKey(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)) { + champApiBuilder.property("champ.event.stream.publisher-pool-size", + daoConfig.get(CONFIG_EVENT_STREAM_NUM_PUBLISHERS)); + } + champApi = champApiBuilder.build(); + } else { + logger.error(CrudServiceMsgs.INVALID_GRAPH_BACKEND, + daoConfig.getProperty(CONFIG_STORAGE_BACKEND_DB)); + } + + } catch (com.thinkaurelius.titan.core.TitanException e) { + + logger.error(CrudServiceMsgs.INSTANTIATE_GRAPH_BACKEND_ERR, "Titan", e.getMessage()); + } + + + break; + + default: + logger.error(CrudServiceMsgs.INVALID_GRAPH_BACKEND, + daoConfig.getProperty(CONFIG_STORAGE_BACKEND)); + break; + } + + } catch (CrudException e) { + logger.error(CrudServiceMsgs.INSTANTIATE_GRAPH_BACKEND_ERR, + daoConfig.getProperty(CONFIG_STORAGE_BACKEND), e.getMessage()); + } + } + + + /** + * Performs any necessary shut down operations when the DAO is no longer needed. + */ + public void close() { + + if (champApi != null) { + + logger.info(CrudServiceMsgs.STOPPING_CHAMP_DAO); + + champApi.shutdown(); + } + } + + + /** + * This helper function converts the 'graph back end type' config parameter into the + * corresponding {@link ChampAPI.Type}. + * + * @return - A {@link ChampAPI.Type} + * @throws CrudException + */ + private ChampGraph.Type getBackendTypeFromConfig() throws CrudException { + + // Get the back end type from the DAO's configuration properties. + String backend = daoConfig.getProperty(CONFIG_STORAGE_BACKEND, "in-memory"); + + // Now, find the appropriate ChampAPI type and return it. + if (backend.equals("in-memory")) { + return ChampGraph.Type.IN_MEMORY; + } else if (backend.equals("titan")) { + return ChampGraph.Type.TITAN; + } + + // If we are here, then whatever was in the config properties didn't match to a supported + // back end type, so just throw an exception and let the caller figure it out. + throw new CrudException("Invalid graph backend type '" + backend + "' specified.", + javax.ws.rs.core.Response.Status.BAD_REQUEST); + } + + +} diff --git a/src/main/java/org/openecomp/crud/entity/Edge.java b/src/main/java/org/openecomp/crud/entity/Edge.java new file mode 100644 index 0000000..803511f --- /dev/null +++ b/src/main/java/org/openecomp/crud/entity/Edge.java @@ -0,0 +1,130 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.entity; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class Edge { + private static final Gson gson = new GsonBuilder().create(); + + private final Optional id; + private final String type; + private final Map properties; + private final Vertex source; + private final Vertex target; + + + private Edge(Builder builder) { + this.id = builder.id; + this.type = builder.type; + this.source = builder.source; + this.target = builder.target; + this.properties = builder.properties; + } + + public static class Builder { + private Optional id = Optional.empty(); + private final String type; + private Vertex source; + private Vertex target; + private final Map properties = new HashMap(); + + public Builder(String type) { + if (type == null) { + throw new IllegalArgumentException("Type cannot be null"); + } + + this.type = type; + } + + public Builder id(String id) { + if (id == null) { + throw new IllegalArgumentException("id cannot be null"); + } + + this.id = Optional.of(id); + return this; + } + + public Builder property(String key, Object value) { + if (key == null || value == null) { + throw new IllegalArgumentException("Property key/value cannot be null"); + } + properties.put(key, value); + return this; + } + + public Builder source(Vertex source) { + this.source = source; + return this; + } + + public Builder target(Vertex target) { + this.target = target; + return this; + } + + public Edge build() { + return new Edge(this); + } + } + + + @Override + public String toString() { + return "Edge [id=" + id + ", type=" + type + ", properties=" + properties + + ", source=" + source + ", target=" + target + "]"; + } + + public String toJson() { + return gson.toJson(this); + } + + public Optional getId() { + return id; + } + + public String getType() { + return type; + } + + public Map getProperties() { + return properties; + } + + public Vertex getSource() { + return source; + } + + public Vertex getTarget() { + return target; + } + + +} diff --git a/src/main/java/org/openecomp/crud/entity/Vertex.java b/src/main/java/org/openecomp/crud/entity/Vertex.java new file mode 100644 index 0000000..1b0b97d --- /dev/null +++ b/src/main/java/org/openecomp/crud/entity/Vertex.java @@ -0,0 +1,102 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.entity; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class Vertex { + private static final Gson gson = new GsonBuilder().create(); + + private final Optional id; + private final String type; + private final Map properties; + + + private Vertex(Builder builder) { + this.id = builder.id; + this.type = builder.type; + this.properties = builder.properties; + } + + public static class Builder { + private Optional id = Optional.empty(); + private final String type; + private final Map properties = new HashMap(); + + public Builder(String type) { + if (type == null) { + throw new IllegalArgumentException("Type cannot be null"); + } + this.type = type; + } + + public Builder id(String id) { + if (id == null) { + throw new IllegalArgumentException("id cannot be null"); + } + + this.id = Optional.of(id); + return this; + } + + public Builder property(String key, Object value) { + if (key == null || value == null) { + throw new IllegalArgumentException("Property key/value cannot be null"); + } + properties.put(key, value); + return this; + } + + public Vertex build() { + return new Vertex(this); + } + } + + public String toJson() { + return gson.toJson(this); + } + + @Override + public String toString() { + return "Vertex [id=" + id + ", type=" + type + ", properties=" + properties + "]"; + } + + public Optional getId() { + return id; + } + + public String getType() { + return type; + } + + public Map getProperties() { + return properties; + } + +} diff --git a/src/main/java/org/openecomp/crud/event/GraphEvent.java b/src/main/java/org/openecomp/crud/event/GraphEvent.java new file mode 100644 index 0000000..392bf2d --- /dev/null +++ b/src/main/java/org/openecomp/crud/event/GraphEvent.java @@ -0,0 +1,235 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.event; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.SerializedName; + +import org.openecomp.crud.exception.CrudException; + +import javax.ws.rs.core.Response.Status; + +public class GraphEvent { + + public enum GraphEventOperation { + CREATE, UPDATE, DELETE + } + + public enum GraphEventResult { + SUCCESS, FAILURE + } + + private GraphEventOperation operation; + + @SerializedName("transaction-id") + private String transactionId; + + private long timestamp; + + private GraphEventVertex vertex; + + private GraphEventEdge edge; + + private GraphEventResult result; + + @SerializedName("error-message") + private String errorMessage; + + private Status httpErrorStatus; + + /** + * Marshaller/unmarshaller for converting to/from JSON. + */ + private static final Gson gson = new GsonBuilder().disableHtmlEscaping() + .setPrettyPrinting().create(); + + public static Builder builder(GraphEventOperation operation) { + return new Builder(operation); + } + + public GraphEventOperation getOperation() { + return operation; + } + + public String getTransactionId() { + return transactionId; + } + + public long getTimestamp() { + return timestamp; + } + + public GraphEventVertex getVertex() { + return vertex; + } + + public GraphEventEdge getEdge() { + return edge; + } + + public GraphEventResult getResult() { + return result; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setResult(GraphEventResult result) { + this.result = result; + } + + + public Status getHttpErrorStatus() { + return httpErrorStatus; + } + + public void setHttpErrorStatus(Status httpErrorStatus) { + this.httpErrorStatus = httpErrorStatus; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public void setVertex(GraphEventVertex vertex) { + this.vertex = vertex; + } + + public void setEdge(GraphEventEdge edge) { + this.edge = edge; + } + + /** + * Unmarshalls this Vertex object into a JSON string. + * + * @return - A JSON format string representation of this Vertex. + */ + public String toJson() { + return gson.toJson(this); + } + + /** + * Marshalls the provided JSON string into a Vertex object. + * + * @param json - The JSON string to produce the Vertex from. + * @return - A Vertex object. + * @throws SpikeException + */ + public static GraphEvent fromJson(String json) throws CrudException { + + try { + + // Make sure that we were actually provided a non-empty string + // before we + // go any further. + if (json == null || json.isEmpty()) { + throw new CrudException("Empty or null JSON string.", Status.BAD_REQUEST); + } + + // Marshall the string into a Vertex object. + return gson.fromJson(json, GraphEvent.class); + + } catch (Exception ex) { + throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST); + } + } + + @Override + public String toString() { + + return toJson(); + } + + public String getObjectKey() { + if (this.getVertex() != null) { + return this.getVertex().getId(); + } else if (this.getEdge() != null) { + return this.getEdge().getId(); + } + + return null; + + } + + public String getObjectType() { + if (this.getVertex() != null) { + return "vertex->" + this.getVertex().getType(); + } else if (this.getEdge() != null) { + return "edge->" + this.getEdge().getType(); + } + + return null; + + } + + public static class Builder { + + GraphEvent event = null; + + public Builder(GraphEventOperation operation) { + event = new GraphEvent(); + event.operation = operation; + } + + public Builder vertex(GraphEventVertex vertex) { + event.vertex = vertex; + return this; + } + + public Builder edge(GraphEventEdge edge) { + event.edge = edge; + return this; + } + + public Builder result(GraphEventResult result) { + event.result = result; + return this; + } + + public Builder errorMessage(String errorMessage) { + event.errorMessage = errorMessage; + return this; + } + + public Builder httpErrorStatus(Status httpErrorStatus) { + event.httpErrorStatus = httpErrorStatus; + return this; + } + + public GraphEvent build() { + + event.timestamp = System.currentTimeMillis(); + event.transactionId = java.util.UUID.randomUUID().toString(); + + return event; + } + } + +} diff --git a/src/main/java/org/openecomp/crud/event/GraphEventEdge.java b/src/main/java/org/openecomp/crud/event/GraphEventEdge.java new file mode 100644 index 0000000..aaf9b72 --- /dev/null +++ b/src/main/java/org/openecomp/crud/event/GraphEventEdge.java @@ -0,0 +1,212 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.event; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; + +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; + +import java.util.Map; +import javax.ws.rs.core.Response.Status; + +/** + * This class provides a generic representation of an Edge as provided by the + * graph data store. + */ +public class GraphEventEdge { + + /** + * The unique identifier used to identify this edge in the graph data store. + */ + @SerializedName("key") + private String id; + + @SerializedName("schema-version") + private String modelVersion; + + /** + * Type label assigned to this vertex. + */ + private String type; + + /** + * Source vertex for our edge. + */ + private GraphEventVertex source; + + /** + * Target vertex for our edge. + */ + private GraphEventVertex target; + + /** + * Map of all of the properties assigned to this vertex. + */ + private JsonElement properties; + + /** + * Marshaller/unmarshaller for converting to/from JSON. + */ + private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + + public GraphEventEdge(String id, String modelVersion, String type, GraphEventVertex source, + GraphEventVertex target, JsonElement properties) { + this.id = id; + this.modelVersion = modelVersion; + this.type = type; + this.source = source; + this.target = target; + this.properties = properties; + } + + public GraphEventEdge() { + + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public GraphEventVertex getSource() { + return source; + } + + public void setSource(GraphEventVertex source) { + this.source = source; + } + + public GraphEventVertex getTarget() { + return target; + } + + public void setTarget(GraphEventVertex target) { + this.target = target; + } + + public JsonElement getProperties() { + return properties; + } + + public void setProperties(JsonElement properties) { + this.properties = properties; + } + + public String getModelVersion() { + return modelVersion; + } + + public void setModelVersion(String modelVersion) { + this.modelVersion = modelVersion; + } + + /** + * Unmarshalls this Edge object into a JSON string. + * + * @return - A JSON format string representation of this Edge. + */ + public String toJson() { + return gson.toJson(this); + } + + /** + * Marshalls the provided JSON string into a Edge object. + * + * @param json - The JSON string to produce the Edge from. + * @return - A Edge object. + * @throws SpikeException + */ + public static GraphEventEdge fromJson(String json) throws CrudException { + + try { + + // Make sure that we were actually provided a non-empty string + // before we + // go any further. + if (json == null || json.isEmpty()) { + throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST); + } + + // Marshall the string into an Edge object. + return gson.fromJson(json, GraphEventEdge.class); + + } catch (Exception ex) { + throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST); + } + } + + public static GraphEventEdge fromEdge(Edge edge, String modelVersion) { + + java.lang.reflect.Type mapType = new TypeToken>() {}.getType(); + JsonObject props = gson.toJsonTree(edge.getProperties(), mapType).getAsJsonObject(); + + GraphEventEdge graphEventEdge = new GraphEventEdge(edge.getId().orElse(""), modelVersion, + edge.getType(), new GraphEventVertex(edge.getSource().getId().orElse(""), null, + edge.getSource().getType(), null), new GraphEventVertex(edge.getTarget().getId().orElse(""), + null, edge.getTarget().getType(), null), props); + + return graphEventEdge; + + } + + public Edge toEdge() { + Edge.Builder builder = new Edge.Builder(this.getType()).id(this.getId()); + if (this.getSource() != null) { + builder.source(new Vertex.Builder(this.getSource().getType()).id(this.getSource().getId()) + .build()); + } + if (this.getTarget() != null) { + builder.target(new Vertex.Builder(this.getTarget().getType()).id(this.getTarget().getId()) + .build()); + } + + if (this.getProperties() != null) { + java.lang.reflect.Type mapType = new TypeToken>() {}.getType(); + Map propertiesMap = gson.fromJson(this.getProperties(), mapType); + for (String key : propertiesMap.keySet()) { + builder.property(key, propertiesMap.get(key)); + } + } + return builder.build(); + + } +} diff --git a/src/main/java/org/openecomp/crud/event/GraphEventVertex.java b/src/main/java/org/openecomp/crud/event/GraphEventVertex.java new file mode 100644 index 0000000..7fde12a --- /dev/null +++ b/src/main/java/org/openecomp/crud/event/GraphEventVertex.java @@ -0,0 +1,181 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.event; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; + +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; + +import java.util.Map; +import javax.ws.rs.core.Response.Status; + +/** + * This class provides a generic representation of a Vertex as provided by the + * graph data store. + */ +public class GraphEventVertex { + + /** + * The unique identifier used to identify this vertex in the graph data + * store. + */ + @SerializedName("key") + private String id; + + @SerializedName("schema-version") + private String modelVersion; + + /** + * Type label assigned to this vertex. + */ + private String type; + + /** + * Map of all of the properties assigned to this vertex. + */ + private JsonElement properties; + + /** + * Marshaller/unmarshaller for converting to/from JSON. + */ + private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + + public GraphEventVertex(String id, String modelVersion, String type, JsonElement properties) { + this.id = id; + this.modelVersion = modelVersion; + this.type = type; + this.properties = properties; + } + + public GraphEventVertex() { + + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + + public JsonElement getProperties() { + return properties; + } + + public void setProperties(JsonElement properties) { + this.properties = properties; + } + + public String getModelVersion() { + return modelVersion; + } + + public void setModelVersion(String modelVersion) { + this.modelVersion = modelVersion; + } + + /** + * Unmarshalls this Vertex object into a JSON string. + * + * @return - A JSON format string representation of this Vertex. + */ + public String toJson() { + return gson.toJson(this); + } + + /** + * Marshalls the provided JSON string into a Vertex object. + * + * @param json - The JSON string to produce the Vertex from. + * @return - A Vertex object. + * @throws SpikeException + */ + public static GraphEventVertex fromJson(String json) throws CrudException { + + try { + + // Make sure that we were actually provided a non-empty string + // before we + // go any further. + if (json == null || json.isEmpty()) { + throw new CrudException("Empty or null JSON string.", Status.BAD_REQUEST); + } + + // Marshall the string into a Vertex object. + return gson.fromJson(json, GraphEventVertex.class); + + } catch (Exception ex) { + throw new CrudException("Unable to parse JSON string: ", Status.BAD_REQUEST); + } + } + + @Override + public String toString() { + + return toJson(); + } + + public static GraphEventVertex fromVertex(Vertex vertex, String modelVersion) { + + java.lang.reflect.Type mapType = new TypeToken>() {}.getType(); + JsonObject props = gson.toJsonTree(vertex.getProperties(), mapType).getAsJsonObject(); + GraphEventVertex graphEventVertex = new GraphEventVertex(vertex.getId().orElse(""), + modelVersion, vertex.getType(), props); + return graphEventVertex; + + } + + public Vertex toVertex() { + Vertex.Builder builder = new Vertex.Builder(this.getType()).id(this.getId()); + + if (this.getProperties() != null) { + java.lang.reflect.Type mapType = new TypeToken>() {}.getType(); + Map propertiesMap = gson.fromJson(this.getProperties(), mapType); + for (String key : propertiesMap.keySet()) { + builder.property(key, propertiesMap.get(key)); + } + } + + return builder.build(); + + } + + +} diff --git a/src/main/java/org/openecomp/crud/exception/CrudException.java b/src/main/java/org/openecomp/crud/exception/CrudException.java new file mode 100644 index 0000000..2911070 --- /dev/null +++ b/src/main/java/org/openecomp/crud/exception/CrudException.java @@ -0,0 +1,62 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.exception; + +import javax.ws.rs.core.Response.Status; + +public class CrudException extends Exception { + + private static final long serialVersionUID = 8162385108397238865L; + + private Status httpStatus; + + public CrudException() { + } + + public CrudException(String message, Status httpStatus) { + super(message); + this.setHttpStatus(httpStatus); + } + + public CrudException(Throwable cause) { + super(cause); + } + + public CrudException(String message, Throwable cause) { + super(message, cause); + } + + public CrudException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public Status getHttpStatus() { + return httpStatus; + } + + public void setHttpStatus(Status httpStatus) { + this.httpStatus = httpStatus; + } +} diff --git a/src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java b/src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java new file mode 100644 index 0000000..71fb14b --- /dev/null +++ b/src/main/java/org/openecomp/crud/logging/CrudServiceMsgs.java @@ -0,0 +1,128 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.logging; + +import com.att.eelf.i18n.EELFResourceManager; + +import org.openecomp.cl.eelf.LogMessageEnum; + +public enum CrudServiceMsgs implements LogMessageEnum { + + /** + * Received request {0} {1} from {2}. Sending response: {3} + * + *

Arguments: + * {0} = operation + * {1} = target URL + * {2} = source + * {3} = response code + */ + PROCESS_REST_REQUEST, + + INVALID_OXM_FILE, + INVALID_OXM_DIR, + OXM_FILE_CHANGED, + + /** + * Successfully loaded schema: {0} + * + *

Arguments: + * {0} = oxm filename + */ + LOADED_OXM_FILE, + + /** + * Unable to load OXM schema: {0} + * + *

Arguments: + * {0} = error + */ + OXM_LOAD_ERROR, + + /** + * Instantiate data access layer for graph data store type: {0} graph: {1} using hosts: {2} + * + *

Arguments: + * {0} = Graph data store technology type + * {1} = Graph name + * {2} = Hosts list + */ + INSTANTIATE_GRAPH_DAO, + + /** + * Stopping ChampDAO... + * + *

Arguments: + */ + STOPPING_CHAMP_DAO, + + /** + * Unsupported graph database {0} specified. + * + *

Arguments: + * {0} = Graph database back end. + */ + INVALID_GRAPH_BACKEND, + + /** + * Failure instantiating {0} graph database backend. Cause: {1} + * + *

Arguments: + * {0} - Graph database type. + * {1} - Failure cause. + */ + INSTANTIATE_GRAPH_BACKEND_ERR, + + /** + * Failure instantiating CRUD Rest Service. Cause: {0} + * + *

Arguments: + * {0} - Failure cause. + */ + INSTANTIATE_AUTH_ERR, + + /** + * Any info log related to titan graph + * + *

Arguments: + * {0} - Info. + */ + TITAN_GRAPH_INFO, + + /** + * Arguments: + * {0} Opertaion + * {1} URI + * {2} = Exception + */ + EXCEPTION_DURING_METHOD_CALL; + + + /** + * Static initializer to ensure the resource bundles for this class are loaded... + */ + static { + EELFResourceManager.loadMessageBundle("logging/CrudServiceMsgs"); + } +} diff --git a/src/main/java/org/openecomp/crud/logging/LoggingUtil.java b/src/main/java/org/openecomp/crud/logging/LoggingUtil.java new file mode 100644 index 0000000..aaa250a --- /dev/null +++ b/src/main/java/org/openecomp/crud/logging/LoggingUtil.java @@ -0,0 +1,88 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.logging; + +import org.openecomp.cl.api.LogFields; +import org.openecomp.cl.api.LogLine; +import org.openecomp.cl.api.Logger; +import org.openecomp.cl.mdc.MdcContext; +import org.openecomp.crud.util.CrudServiceConstants; +import org.slf4j.MDC; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; + + +public class LoggingUtil { + /** + * Initializes mdc context. + */ + public static void initMdcContext(HttpServletRequest httpReq, HttpHeaders headers) { + String fromIp = httpReq.getRemoteAddr(); + String fromAppId = ""; + String transId = null; + + if (headers.getRequestHeaders().getFirst("X-FromAppId") != null) { + fromAppId = headers.getRequestHeaders().getFirst("X-FromAppId"); + } + + if ((headers.getRequestHeaders().getFirst("X-TransactionId") == null) + || headers.getRequestHeaders().getFirst("X-TransactionId").isEmpty()) { + transId = java.util.UUID.randomUUID().toString(); + } else { + transId = headers.getRequestHeaders().getFirst("X-TransactionId"); + } + + MdcContext.initialize(transId, CrudServiceConstants.CRD_SERVICE_NAME, "", fromAppId, fromIp); + } + + /** + * Logs the rest request. + */ + public static void logRestRequest(Logger logger, Logger auditLogger, + HttpServletRequest req, Response response) { + String respStatusString = ""; + if (Response.Status.fromStatusCode(response.getStatus()) != null) { + respStatusString = Response.Status.fromStatusCode(response.getStatus()).toString(); + } + + // Generate error log + logger.info(CrudServiceMsgs.PROCESS_REST_REQUEST, req.getMethod(), + req.getRequestURL().toString(), req.getRemoteHost(), + Integer.toString(response.getStatus())); + + // Generate audit log. + auditLogger.info(CrudServiceMsgs.PROCESS_REST_REQUEST, + new LogFields() + .setField(LogLine.DefinedFields.RESPONSE_CODE, response.getStatus()) + .setField(LogLine.DefinedFields.RESPONSE_DESCRIPTION, respStatusString), + (req != null) ? req.getMethod() : "Unknown", + (req != null) ? req.getRequestURL().toString() : "Unknown", + (req != null) ? req.getRemoteHost() : "Unknown", + Integer.toString(response.getStatus()) + " error: " + (response.getEntity() == null ? "" + : response.getEntity().toString())); + MDC.clear(); + } +} diff --git a/src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java b/src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java new file mode 100644 index 0000000..1a84322 --- /dev/null +++ b/src/main/java/org/openecomp/crud/parser/CrudResponseBuilder.java @@ -0,0 +1,185 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.parser; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.service.EdgePayload; +import org.openecomp.crud.service.VertexPayload; +import org.openecomp.schema.RelationshipSchemaLoader; + +import java.util.ArrayList; +import java.util.List; + +public class CrudResponseBuilder { + + private static final Gson gson = new GsonBuilder().create(); + + public static final String SOURCE = "source"; + public static final String TARGET = "target"; + public static final String URL_BASE = "services/inventory/"; + + public static String buildUpsertVertexResponse(Vertex vertex, String version) + throws CrudException { + VertexPayload payload = new VertexPayload(); + payload.setId(vertex.getId().get()); + payload.setType(vertex.getType()); + payload.setUrl(URL_BASE + version + "/" + vertex.getType() + "/" + vertex.getId().get()); + JsonObject props = new JsonObject(); + for (String key : vertex.getProperties().keySet()) { + addJsonProperperty(props, key, vertex.getProperties().get(key)); + } + payload.setProperties(props); + return payload.toJson(); + } + + public static String buildUpsertEdgeResponse(Edge edge, String version) throws CrudException { + return buildGetEdgeResponse(edge, version); + } + + public static String buildGetVertexResponse(Vertex vertex, List edges, String version) + throws CrudException { + VertexPayload vertexPayload = new VertexPayload(); + vertexPayload.setId(vertex.getId().get()); + vertexPayload.setType(vertex.getType()); + vertexPayload.setUrl(URL_BASE + version + "/" + vertex.getType() + "/" + vertex.getId().get()); + JsonObject props = new JsonObject(); + for (String key : vertex.getProperties().keySet()) { + addJsonProperperty(props, key, vertex.getProperties().get(key)); + } + vertexPayload.setProperties(props); + List inEdges = new ArrayList(); + List outEdges = new ArrayList(); + for (Edge e : edges) { + if (e.getTarget().getId().get().equals(vertex.getId().get())) { + EdgePayload inEdge = new EdgePayload(); + inEdge.setId(e.getId().get()); + inEdge.setType(e.getType()); + inEdge.setUrl(URL_BASE + "relationships/" + + RelationshipSchemaLoader.getLatestSchemaVersion() + + "/" + e.getType() + "/" + e.getId().get()); + inEdge.setSource( + URL_BASE + version + "/" + e.getSource().getType() + "/" + e.getSource().getId().get()); + + inEdges.add(inEdge); + } else if (e.getSource().getId().get().equals(vertex.getId().get())) { + EdgePayload outEdge = new EdgePayload(); + outEdge.setId(e.getId().get()); + outEdge.setType(e.getType()); + outEdge.setUrl(URL_BASE + "relationships/" + + RelationshipSchemaLoader.getLatestSchemaVersion() + + "/" + e.getType() + "/" + e.getId().get()); + outEdge.setTarget( + URL_BASE + version + "/" + e.getTarget().getType() + "/" + e.getTarget().getId().get()); + outEdges.add(outEdge); + } + } + + + vertexPayload.setIn(inEdges); + vertexPayload.setOut(outEdges); + + return vertexPayload.toJson(); + } + + public static String buildGetVerticesResponse(List items, String version) + throws CrudException { + + JsonArray arry = new JsonArray(); + for (Vertex v : items) { + JsonObject item = new JsonObject(); + item.addProperty("id", v.getId().get()); + item.addProperty("type", v.getType()); + item.addProperty("url", "services/inventory/" + version + "/" + + v.getType() + "/" + v.getId().get()); + + arry.add(item); + } + + return gson.toJson(arry); + } + + public static String buildGetEdgeResponse(Edge edge, String version) throws CrudException { + + EdgePayload payload = new EdgePayload(); + payload.setId(edge.getId().get()); + payload.setType(edge.getType()); + payload.setUrl(URL_BASE + "relationships/" + version + "/" + edge.getType() + + "/" + edge.getId().get()); + payload.setSource( + URL_BASE + version + "/" + edge.getSource().getType() + + "/" + edge.getSource().getId().get()); + payload.setTarget( + URL_BASE + version + "/" + edge.getTarget().getType() + + "/" + edge.getTarget().getId().get()); + + JsonObject props = new JsonObject(); + for (String key : edge.getProperties().keySet()) { + addJsonProperperty(props, key, edge.getProperties().get(key)); + } + payload.setProperties(props); + return payload.toJson(); + } + + public static String buildGetEdgesResponse(List items, String version) + throws CrudException { + + JsonArray arry = new JsonArray(); + for (Edge e : items) { + JsonObject item = new JsonObject(); + item.addProperty("id", e.getId().get()); + item.addProperty("type", e.getType()); + item.addProperty("url", URL_BASE + "relationships/" + version + "/" + e.getType() + + "/" + e.getId().get()); + item.addProperty(SOURCE, "services/inventory/" + version + "/" + e.getSource().getType() + + "/" + e.getSource().getId().get()); + item.addProperty(TARGET, "services/inventory/" + version + "/" + e.getTarget().getType() + + "/" + e.getTarget().getId().get()); + arry.add(item); + } + + return gson.toJson(arry); + } + + private static void addJsonProperperty(JsonObject jsonObj, String key, Object value) { + if (value instanceof Integer) { + jsonObj.addProperty(key, (Integer) value); + } else if (value instanceof Boolean) { + jsonObj.addProperty(key, (Boolean) value); + } else if (value instanceof Double) { + jsonObj.addProperty(key, (Double) value); + } else if (value instanceof String) { + jsonObj.addProperty(key, (String) value); + } else { + jsonObj.addProperty(key, value.toString()); + } + } + +} diff --git a/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java b/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java new file mode 100644 index 0000000..ce60c3c --- /dev/null +++ b/src/main/java/org/openecomp/crud/service/CrudGraphDataService.java @@ -0,0 +1,209 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.service; + +import com.att.ecomp.event.api.EventPublisher; + +import org.openecomp.crud.dao.GraphDao; +import org.openecomp.crud.dao.champ.ChampDao; +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.parser.CrudResponseBuilder; +import org.openecomp.crud.util.CrudProperties; +import org.openecomp.crud.util.CrudServiceConstants; +import org.openecomp.schema.OxmModelLoader; +import org.openecomp.schema.OxmModelValidator; +import org.openecomp.schema.RelationshipSchemaLoader; +import org.openecomp.schema.RelationshipSchemaValidator; + +import java.util.List; +import java.util.Map; +import java.util.Properties; + +public class CrudGraphDataService { + + private GraphDao dao; + + public CrudGraphDataService(EventPublisher champEventPublisher) throws CrudException { + + // Configure the GraphDao and wire it + Properties champProperties = new Properties(); + champProperties.put(ChampDao.CONFIG_STORAGE_BACKEND, "titan"); + champProperties.put(ChampDao.CONFIG_STORAGE_BACKEND_DB, + CrudProperties.get(CrudServiceConstants.CRD_STORAGE_BACKEND_DB, "hbase")); + champProperties.put(ChampDao.CONFIG_STORAGE_HOSTNAMES, + CrudProperties.get(CrudServiceConstants.CRD_GRAPH_HOST)); + champProperties.put(ChampDao.CONFIG_STORAGE_PORT, + CrudProperties.get(CrudServiceConstants.CRD_GRAPH_PORT, "2181")); + champProperties.put(ChampDao.CONFIG_HBASE_ZNODE_PARENT, + CrudProperties.get(CrudServiceConstants.CRD_HBASE_ZNODE_PARENT, "/hbase-unsecure")); + + if (CrudProperties.get("crud.graph.name") != null) { + champProperties.put(ChampDao.CONFIG_GRAPH_NAME, CrudProperties.get("crud.graph.name")); + } + + if (champEventPublisher != null) { + champProperties.put(ChampDao.CONFIG_EVENT_STREAM_PUBLISHER, champEventPublisher); + } + + if (CrudProperties.get(ChampDao.CONFIG_EVENT_STREAM_NUM_PUBLISHERS) != null) { + champProperties.put(ChampDao.CONFIG_EVENT_STREAM_NUM_PUBLISHERS, + Integer.parseInt(CrudProperties.get(ChampDao.CONFIG_EVENT_STREAM_NUM_PUBLISHERS))); + } + + ChampDao champDao = new ChampDao(champProperties); + + this.dao = champDao; + + //load the schemas + OxmModelLoader.loadModels(); + RelationshipSchemaLoader.loadModels(); + } + + + public String addVertex(String version, String type, VertexPayload payload) throws CrudException { + Vertex vertex = OxmModelValidator.validateIncomingUpsertPayload(null, version, type, + payload.getProperties()); + return addVertex(version, vertex); + } + + private String addVertex(String version, Vertex vertex) throws CrudException { + Vertex addedVertex = dao.addVertex(vertex.getType(), vertex.getProperties()); + return CrudResponseBuilder + .buildUpsertVertexResponse(OxmModelValidator.validateOutgoingPayload(version, addedVertex), + version); + } + + public String addEdge(String version, String type, EdgePayload payload) throws CrudException { + Edge edge = RelationshipSchemaValidator.validateIncomingAddPayload(version, type, payload); + return addEdge(version, edge); + } + + private String addEdge(String version, Edge edge) throws CrudException { + Edge addedEdge = dao.addEdge(edge.getType(), edge.getSource(), edge.getTarget(), + edge.getProperties()); + return CrudResponseBuilder.buildUpsertEdgeResponse( + RelationshipSchemaValidator.validateOutgoingPayload(version, addedEdge), version); + } + + public String getEdge(String version, String id, String type) throws CrudException { + RelationshipSchemaValidator.validateType(version, type); + Edge edge = dao.getEdge(id, type); + + return CrudResponseBuilder.buildGetEdgeResponse(RelationshipSchemaValidator + .validateOutgoingPayload(version, edge), + version); + } + + public String getEdges(String version, String type, Map filter) + throws CrudException { + RelationshipSchemaValidator.validateType(version, type); + List items = dao.getEdges(type, RelationshipSchemaValidator + .resolveCollectionfilter(version, type, filter)); + return CrudResponseBuilder.buildGetEdgesResponse(items, version); + } + + + public String updateVertex(String version, String id, String type, VertexPayload payload) + throws CrudException { + Vertex vertex = OxmModelValidator.validateIncomingUpsertPayload(id, version, type, + payload.getProperties()); + return updateVertex(version, vertex); + + } + + private String updateVertex(String version, Vertex vertex) throws CrudException { + Vertex updatedVertex = dao.updateVertex(vertex.getId().get(), vertex.getType(), + vertex.getProperties()); + return CrudResponseBuilder + .buildUpsertVertexResponse(OxmModelValidator.validateOutgoingPayload(version, + updatedVertex), version); + } + + public String patchVertex(String version, String id, String type, VertexPayload payload) + throws CrudException { + Vertex existingVertex = dao.getVertex(id, OxmModelValidator.resolveCollectionType(version, + type)); + Vertex vertex = OxmModelValidator.validateIncomingPatchPayload(id, version, type, + payload.getProperties(), existingVertex); + return updateVertex(version, vertex); + + } + + public String deleteVertex(String version, String id, String type) throws CrudException { + type = OxmModelValidator.resolveCollectionType(version, type); + dao.deleteVertex(id, type); + return ""; + + } + + public String deleteEdge(String version, String id, String type) throws CrudException { + RelationshipSchemaValidator.validateType(version, type); + dao.deleteEdge(id, type); + return ""; + + } + + public String updateEdge(String version, String id, String type, EdgePayload payload) + throws CrudException { + Edge edge = dao.getEdge(id, type); + Edge validatedEdge = RelationshipSchemaValidator.validateIncomingUpdatePayload(edge, + version, payload); + return updateEdge(version, validatedEdge); + + } + + private String updateEdge(String version, Edge edge) throws CrudException { + Edge updatedEdge = dao.updateEdge(edge); + return CrudResponseBuilder.buildUpsertEdgeResponse( + RelationshipSchemaValidator.validateOutgoingPayload(version, updatedEdge), version); + } + + public String patchEdge(String version, String id, String type, EdgePayload payload) + throws CrudException { + Edge edge = dao.getEdge(id, type); + Edge patchedEdge = RelationshipSchemaValidator.validateIncomingPatchPayload(edge, + version, payload); + return updateEdge(version, patchedEdge); + + } + + public String getVertex(String version, String id, String type) throws CrudException { + type = OxmModelValidator.resolveCollectionType(version, type); + Vertex vertex = dao.getVertex(id, type); + List edges = dao.getVertexEdges(id); + return CrudResponseBuilder.buildGetVertexResponse(OxmModelValidator + .validateOutgoingPayload(version, vertex), edges, version); + } + + public String getVertices(String version, String type, Map filter) + throws CrudException { + type = OxmModelValidator.resolveCollectionType(version, type); + List items = dao.getVertices(type, OxmModelValidator.resolveCollectionfilter(version, + type, filter)); + return CrudResponseBuilder.buildGetVerticesResponse(items, version); + } + +} diff --git a/src/main/java/org/openecomp/crud/service/CrudRestService.java b/src/main/java/org/openecomp/crud/service/CrudRestService.java new file mode 100644 index 0000000..09193ad --- /dev/null +++ b/src/main/java/org/openecomp/crud/service/CrudRestService.java @@ -0,0 +1,686 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.service; + +import org.apache.cxf.jaxrs.ext.PATCH; +import org.openecomp.auth.Auth; +import org.openecomp.cl.api.Logger; +import org.openecomp.cl.eelf.LoggerFactory; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.logging.CrudServiceMsgs; +import org.openecomp.crud.logging.LoggingUtil; +import org.openecomp.crud.util.CrudServiceConstants; +import org.slf4j.MDC; + +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.security.auth.x500.X500Principal; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.Encoded; +import javax.ws.rs.GET; +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.core.Context; +import javax.ws.rs.core.HttpHeaders; +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; + +public class CrudRestService { + + private CrudGraphDataService crudGraphDataService; + Logger logger = LoggerFactory.getInstance().getLogger(CrudRestService.class.getName()); + Logger auditLogger = LoggerFactory.getInstance().getAuditLogger(CrudRestService.class.getName()); + private Auth auth; + + private String mediaType = MediaType.APPLICATION_JSON; + public static final String HTTP_PATCH_METHOD_OVERRIDE = "X-HTTP-Method-Override"; + + public CrudRestService(CrudGraphDataService crudGraphDataService) throws Exception { + this.crudGraphDataService = crudGraphDataService; + this.auth = new Auth(CrudServiceConstants.CRD_AUTH_FILE); + } + + public enum Action { + POST, GET, PUT, DELETE, PATCH + } + + ; + + public void startup() { + + } + + @GET + @Path("/{version}/{type}/{id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response getVertex(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + String result = crudGraphDataService.getVertex(version, id, type); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @GET + @Path("/{version}/{type}/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response getVertices(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("uri") @Encoded String uri, + @Context HttpHeaders headers, @Context UriInfo uriInfo, + @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + Map filter = new HashMap(); + for (Map.Entry> e : uriInfo.getQueryParameters().entrySet()) { + filter.put(e.getKey(), e.getValue().get(0)); + } + + try { + String result = crudGraphDataService.getVertices(version, type, filter); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @GET + @Path("/relationships/{version}/{type}/{id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response getEdge(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + + String result = crudGraphDataService.getEdge(version, id, type); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @GET + @Path("/relationships/{version}/{type}/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response getEdges(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("uri") @Encoded String uri, + @Context HttpHeaders headers, @Context UriInfo uriInfo, + @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.GET, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + Map filter = new HashMap(); + for (Map.Entry> e : uriInfo.getQueryParameters().entrySet()) { + filter.put(e.getKey(), e.getValue().get(0)); + } + + try { + String result = crudGraphDataService.getEdges(version, type, filter); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @PUT + @Path("/relationships/{version}/{type}/{id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response updateEdge(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + EdgePayload payload = EdgePayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null && !payload.getId().equals(id)) { + throw new CrudException("ID Mismatch", Status.BAD_REQUEST); + } + String result; + + if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null + && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) + .equalsIgnoreCase("PATCH")) { + result = crudGraphDataService.patchEdge(version, id, type, payload); + } else { + + result = crudGraphDataService.updateEdge(version, id, type, payload); + } + + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @PATCH + @Path("/relationships/{version}/{type}/{id}") + @Consumes({"application/merge-patch+json"}) + @Produces({MediaType.APPLICATION_JSON}) + public Response patchEdge(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + if (validateRequest(req, uri, content, Action.PATCH, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + EdgePayload payload = EdgePayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null && !payload.getId().equals(id)) { + throw new CrudException("ID Mismatch", Status.BAD_REQUEST); + } + + String result = crudGraphDataService.patchEdge(version, id, type, payload); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @PUT + @Path("/{version}/{type}/{id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response updateVertex(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.PUT, CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + VertexPayload payload = VertexPayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null && !payload.getId().equals(id)) { + throw new CrudException("ID Mismatch", Status.BAD_REQUEST); + } + String result; + if (headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) != null + && headers.getRequestHeaders().getFirst(HTTP_PATCH_METHOD_OVERRIDE) + .equalsIgnoreCase("PATCH")) { + result = crudGraphDataService.patchVertex(version, id, type, payload); + } else { + + result = crudGraphDataService.updateVertex(version, id, type, payload); + } + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @PATCH + @Path("/{version}/{type}/{id}") + @Consumes({"application/merge-patch+json"}) + @Produces({MediaType.APPLICATION_JSON}) + public Response patchVertex(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.PATCH, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + try { + VertexPayload payload = VertexPayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null && !payload.getId().equals(id)) { + throw new CrudException("ID Mismatch", Status.BAD_REQUEST); + } + + String result = crudGraphDataService.patchVertex(version, id, type, payload); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @POST + @Path("/{version}/{type}/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response addVertex(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("uri") @Encoded String uri, + @Context HttpHeaders headers, @Context UriInfo uriInfo, + @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.POST, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + VertexPayload payload = VertexPayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null) { + throw new CrudException("ID specified , use Http PUT to update Vertex", + Status.BAD_REQUEST); + } + + if (payload.getType() != null && !payload.getType().equals(type)) { + throw new CrudException("Vertex Type mismatch", Status.BAD_REQUEST); + } + + String result = crudGraphDataService.addVertex(version, type, payload); + response = Response.status(Status.CREATED).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @POST + @Path("/{version}/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response addVertex(String content, @PathParam("version") String version, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.POST, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + try { + + VertexPayload payload = VertexPayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null) { + throw new CrudException("ID specified , use Http PUT to update Vertex", + Status.BAD_REQUEST); + } + + if (payload.getType() == null || payload.getType().isEmpty()) { + throw new CrudException("Missing Vertex Type ", Status.BAD_REQUEST); + } + String result = crudGraphDataService.addVertex(version, payload.getType(), payload); + response = Response.status(Status.CREATED).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @POST + @Path("/relationships/{version}/{type}/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response addEdge(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("uri") @Encoded String uri, + @Context HttpHeaders headers, @Context UriInfo uriInfo, + @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.POST, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + + try { + EdgePayload payload = EdgePayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null) { + throw new CrudException("ID specified , use Http PUT to update Edge", Status.BAD_REQUEST); + } + + if (payload.getType() != null && !payload.getType().equals(type)) { + throw new CrudException("Edge Type mismatch", Status.BAD_REQUEST); + } + String result = crudGraphDataService.addEdge(version, type, payload); + response = Response.status(Status.CREATED).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @POST + @Path("/relationships/{version}/") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response addEdge(String content, @PathParam("version") String version, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.POST, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + + try { + EdgePayload payload = EdgePayload.fromJson(content); + if (payload.getProperties() == null || payload.getProperties().isJsonNull()) { + throw new CrudException("Invalid request Payload", Status.BAD_REQUEST); + } + if (payload.getId() != null) { + throw new CrudException("ID specified , use Http PUT to update Edge", Status.BAD_REQUEST); + } + + if (payload.getType() == null || payload.getType().isEmpty()) { + throw new CrudException("Missing Edge Type ", Status.BAD_REQUEST); + } + String result = crudGraphDataService.addEdge(version, payload.getType(), payload); + + response = Response.status(Status.CREATED).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @DELETE + @Path("/{version}/{type}/{id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response deleteVertex(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + + if (validateRequest(req, uri, content, Action.DELETE, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + + try { + String result = crudGraphDataService.deleteVertex(version, id, type); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + @DELETE + @Path("/relationships/{version}/{type}/{id}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public Response deleteEdge(String content, @PathParam("version") String version, + @PathParam("type") String type, @PathParam("id") String id, + @PathParam("uri") @Encoded String uri, @Context HttpHeaders headers, + @Context UriInfo uriInfo, @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + + logger.debug("Incoming request..." + content); + Response response = null; + if (validateRequest(req, uri, content, Action.DELETE, + CrudServiceConstants.CRD_AUTH_POLICY_NAME)) { + + try { + String result = crudGraphDataService.deleteEdge(version, id, type); + response = Response.status(Status.OK).entity(result).type(mediaType).build(); + } catch (CrudException ce) { + response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build(); + } catch (Exception e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } else { + response = Response.status(Status.FORBIDDEN).entity(content) + .type(MediaType.APPLICATION_JSON).build(); + } + + LoggingUtil.logRestRequest(logger, auditLogger, req, response); + return response; + } + + protected boolean validateRequest(HttpServletRequest req, String uri, String content, + Action action, String authPolicyFunctionName) { + try { + String cipherSuite = (String) req.getAttribute("javax.servlet.request.cipher_suite"); + String authUser = null; + if (cipherSuite != null) { + X509Certificate[] certChain = (X509Certificate[]) req + .getAttribute("javax.servlet.request.X509Certificate"); + X509Certificate clientCert = certChain[0]; + X500Principal subjectDn = clientCert.getSubjectX500Principal(); + authUser = subjectDn.toString(); + } + return this.auth.validateRequest(authUser.toLowerCase(), action.toString() + + ":" + authPolicyFunctionName); + } catch (Exception e) { + logResult(action, uri, e); + return false; + } + } + + void logResult(Action op, String uri, Exception e) { + + logger.error(CrudServiceMsgs.EXCEPTION_DURING_METHOD_CALL, op.toString(), uri, + e.getStackTrace().toString()); + + // Clear the MDC context so that no other transaction inadvertently + // uses our transaction id. + MDC.clear(); + } +} diff --git a/src/main/java/org/openecomp/crud/service/EdgePayload.java b/src/main/java/org/openecomp/crud/service/EdgePayload.java new file mode 100644 index 0000000..d098d16 --- /dev/null +++ b/src/main/java/org/openecomp/crud/service/EdgePayload.java @@ -0,0 +1,115 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.service; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; + +import org.openecomp.crud.exception.CrudException; + +import javax.ws.rs.core.Response.Status; + +public class EdgePayload { + + private String id; + private String type; + private String url; + private String source; + private String target; + private JsonElement properties; + + private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + + + @Override + public String toString() { + return "EdgePayload [id=" + id + ", type=" + type + ", url=" + url + ", source=" + + source + ", target=" + target + ", properties=" + properties + "]"; + } + + public String toJson() { + return gson.toJson(this); + } + + public static EdgePayload fromJson(String payload) throws CrudException { + try { + if (payload == null || payload.isEmpty()) { + throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST); + } + return gson.fromJson(payload, EdgePayload.class); + } catch (Exception ex) { + throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST); + } + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + + public JsonElement getProperties() { + return properties; + } + + public void setProperties(JsonElement properties) { + this.properties = properties; + } + +} \ No newline at end of file diff --git a/src/main/java/org/openecomp/crud/service/JaxrsEchoService.java b/src/main/java/org/openecomp/crud/service/JaxrsEchoService.java new file mode 100644 index 0000000..0edd316 --- /dev/null +++ b/src/main/java/org/openecomp/crud/service/JaxrsEchoService.java @@ -0,0 +1,63 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.service; + +import org.openecomp.cl.api.Logger; +import org.openecomp.cl.eelf.LoggerFactory; +import org.openecomp.crud.logging.LoggingUtil; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; + + +public class JaxrsEchoService { + + private static Logger logger = LoggerFactory.getInstance() + .getLogger(JaxrsEchoService.class.getName()); + private static Logger auditLogger = LoggerFactory.getInstance() + .getAuditLogger(JaxrsEchoService.class.getName()); + + @GET + @Path("echo/{input}") + @Produces("text/plain") + public String ping(@PathParam("input") String input, + @Context HttpHeaders headers, + @Context UriInfo info, + @Context HttpServletRequest req) { + + LoggingUtil.initMdcContext(req, headers); + LoggingUtil.logRestRequest(logger, auditLogger, req, Response.status(Status.OK) + .entity("OK").build()); + + return "Hello, " + input + "."; + } +} \ No newline at end of file diff --git a/src/main/java/org/openecomp/crud/service/VertexPayload.java b/src/main/java/org/openecomp/crud/service/VertexPayload.java new file mode 100644 index 0000000..ed79002 --- /dev/null +++ b/src/main/java/org/openecomp/crud/service/VertexPayload.java @@ -0,0 +1,117 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.service; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; + +import org.openecomp.crud.exception.CrudException; + +import java.util.ArrayList; +import java.util.List; +import javax.ws.rs.core.Response.Status; + +public class VertexPayload { + + private String id; + private String type; + private String url; + private JsonElement properties; + private List in = new ArrayList(); + private List out = new ArrayList(); + + private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + + public String toJson() { + return gson.toJson(this); + } + + public static VertexPayload fromJson(String payload) throws CrudException { + try { + if (payload == null || payload.isEmpty()) { + throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST); + } + return gson.fromJson(payload, VertexPayload.class); + } catch (Exception ex) { + throw new CrudException("Invalid Json Payload", Status.BAD_REQUEST); + } + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public JsonElement getProperties() { + return properties; + } + + public void setProperties(JsonElement properties) { + this.properties = properties; + } + + public List getIn() { + return in; + } + + public void setIn(List in) { + this.in = in; + } + + public List getOut() { + return out; + } + + public void setOut(List out) { + this.out = out; + } + + + @Override + public String toString() { + return "VertexPayload [id=" + id + ", type=" + type + ", url=" + url + ", properties=" + + properties + ", in=" + in + ", out=" + out + "]"; + } + +} \ No newline at end of file diff --git a/src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java b/src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java new file mode 100644 index 0000000..c1a1e18 --- /dev/null +++ b/src/main/java/org/openecomp/crud/util/CrudJaxbTransformation.java @@ -0,0 +1,92 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.util; + +import org.eclipse.persistence.dynamic.DynamicEntity; +import org.eclipse.persistence.jaxb.JAXBMarshaller; +import org.eclipse.persistence.jaxb.MarshallerProperties; +import org.eclipse.persistence.jaxb.UnmarshallerProperties; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; + +import java.io.StringReader; +import java.io.StringWriter; +import javax.ws.rs.core.MediaType; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.transform.stream.StreamSource; + +public class CrudJaxbTransformation { + /** + * Marshal a dynamic entity into a string. + * + * @param entity the dynamic entity + * @param jaxbContext the dynamic jaxb context + * @return the marshaled entity + * @throws RouterException on error + */ + public static String marshal(MediaType mediaType, final DynamicEntity entity, + final DynamicJAXBContext jaxbContext) throws JAXBException { + + final JAXBMarshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty(JAXBMarshaller.JAXB_FORMATTED_OUTPUT, false); + + if (MediaType.APPLICATION_JSON_TYPE.isCompatible(mediaType)) { + marshaller.setProperty("eclipselink.media-type", "application/json"); + marshaller.setProperty("eclipselink.json.include-root", false); + marshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, Boolean.FALSE); + } + + final StringWriter writer = new StringWriter(); + marshaller.marshal(entity, writer); + return writer.toString(); + + } + + /** + * @param type + * @param json + * @param mediaType + * @return + * @throws JAXBException + * @throws Exception + */ + public static Object unmarshal(String javaClass, String content, MediaType mediaType, + final DynamicJAXBContext jaxbContext) throws JAXBException { + Object clazz = null; + Unmarshaller unmarshaller = null; + + clazz = jaxbContext.newDynamicEntity(javaClass); + + unmarshaller = jaxbContext.createUnmarshaller(); + if (mediaType.equals(MediaType.APPLICATION_JSON_TYPE)) { + unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json"); + unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false); + unmarshaller.setProperty(UnmarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true); + } + + return unmarshaller.unmarshal(new StreamSource(new StringReader(content)), + clazz.getClass()).getValue(); + } + +} diff --git a/src/main/java/org/openecomp/crud/util/CrudProperties.java b/src/main/java/org/openecomp/crud/util/CrudProperties.java new file mode 100644 index 0000000..69b2e16 --- /dev/null +++ b/src/main/java/org/openecomp/crud/util/CrudProperties.java @@ -0,0 +1,77 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; + +public class CrudProperties { + + private static Properties properties; + + static { + properties = new Properties(); + File file = new File(CrudServiceConstants.CRD_CONFIG_FILE); + try { + properties.load(new FileInputStream(file)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static String get(String key) { + return properties.getProperty(key); + } + + public static String get(String key, String defaultValue) { + return properties.getProperty(key, defaultValue); + } + + public static void put(String key, String value) { + properties.setProperty(key, value); + FileOutputStream fileOut = null; + try { + fileOut = new FileOutputStream(new File(CrudServiceConstants.CRD_CONFIG_FILE)); + properties.store(fileOut, "Added property: " + key); + } catch (Exception e) { + e.printStackTrace(); + } finally { + + try { + fileOut.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + } + + +} diff --git a/src/main/java/org/openecomp/crud/util/CrudServiceConstants.java b/src/main/java/org/openecomp/crud/util/CrudServiceConstants.java new file mode 100644 index 0000000..9543e2d --- /dev/null +++ b/src/main/java/org/openecomp/crud/util/CrudServiceConstants.java @@ -0,0 +1,53 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.util; + +public class CrudServiceConstants { + public static final String CRD_SERVICE_NAME = "Crud-Service"; + + public static final String CRD_FILESEP = (System.getProperty("file.separator") == null) ? "/" + : System.getProperty("file.separator"); + + public static final String CRD_SPECIFIC_CONFIG = System.getProperty("CONFIG_HOME") + CRD_FILESEP; + + public static final String CRD_HOME_MODEL = CRD_SPECIFIC_CONFIG + "model" + CRD_FILESEP; + public static final String CRD_HOME_AUTH = CRD_SPECIFIC_CONFIG + "auth" + CRD_FILESEP; + + public static final String CRD_GRAPH_HOST = "crud.graph.host"; + public static final String CRD_GRAPH_PORT = "crud.graph.port"; + public static final String CRD_GRAPH_NAME = "crud.graph.name"; + public static final String CRD_STORAGE_BACKEND_DB = "crud.storage.backend.db"; + public static final String CRD_HBASE_ZNODE_PARENT + = "crud.storage.hbase.ext.zookeeper.znode.parent"; + + public static final String CRD_CONFIG_FILE = CRD_SPECIFIC_CONFIG + "crud-api.properties"; + public static final String CRD_AUTH_FILE = CRD_HOME_AUTH + "crud_policy.json"; + + public static final String CRD_AUTH_POLICY_NAME = "crud"; + + public static final String CRD_EVENT_STREAM_HOSTS = "event.stream.hosts"; + + + +} diff --git a/src/main/java/org/openecomp/crud/util/CrudServiceUtil.java b/src/main/java/org/openecomp/crud/util/CrudServiceUtil.java new file mode 100644 index 0000000..25e3de6 --- /dev/null +++ b/src/main/java/org/openecomp/crud/util/CrudServiceUtil.java @@ -0,0 +1,56 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.util; + +import org.openecomp.crud.exception.CrudException; + +import javax.ws.rs.core.Response.Status; + +public class CrudServiceUtil { + + + public static Object validateFieldType(String value, Class clazz) throws CrudException { + try { + if (clazz.isAssignableFrom(Integer.class)) { + return Integer.parseInt(value); + } else if (clazz.isAssignableFrom(Long.class)) { + return Long.parseLong(value); + } else if (clazz.isAssignableFrom(Float.class)) { + return Float.parseFloat(value); + } else if (clazz.isAssignableFrom(Double.class)) { + return Double.parseDouble(value); + } else if (clazz.isAssignableFrom(Boolean.class)) { + if (!value.equals("true") && !value.equals("false")) { + throw new CrudException("Invalid propertry value: " + value, Status.BAD_REQUEST); + } + return Boolean.parseBoolean(value); + } else { + return value; + } + } catch (Exception e) { + throw new CrudException("Invalid property value: " + value, Status.BAD_REQUEST); + } + } + +} diff --git a/src/main/java/org/openecomp/crud/util/FileWatcher.java b/src/main/java/org/openecomp/crud/util/FileWatcher.java new file mode 100644 index 0000000..8c7cbc1 --- /dev/null +++ b/src/main/java/org/openecomp/crud/util/FileWatcher.java @@ -0,0 +1,48 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.crud.util; + +import java.io.File; +import java.util.TimerTask; + +public abstract class FileWatcher extends TimerTask { + private long timeStamp; + private File file; + + public FileWatcher(File file) { + this.file = file; + this.timeStamp = file.lastModified(); + } + + public final void run() { + long timeStamp = file.lastModified(); + + if ((timeStamp - this.timeStamp) > 500) { + this.timeStamp = timeStamp; + onChange(file); + } + } + + protected abstract void onChange(File file); +} \ No newline at end of file diff --git a/src/main/java/org/openecomp/schema/OxmModelLoader.java b/src/main/java/org/openecomp/schema/OxmModelLoader.java new file mode 100644 index 0000000..4ef77a2 --- /dev/null +++ b/src/main/java/org/openecomp/schema/OxmModelLoader.java @@ -0,0 +1,168 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.schema; + +import org.eclipse.persistence.jaxb.JAXBContextProperties; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory; +import org.openecomp.cl.eelf.LoggerFactory; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.logging.CrudServiceMsgs; +import org.openecomp.crud.util.CrudServiceConstants; +import org.openecomp.crud.util.FileWatcher; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.ws.rs.core.Response.Status; +import javax.xml.bind.JAXBException; + + +public class OxmModelLoader { + + private static Map versionContextMap + = new ConcurrentHashMap(); + private static Map timers = new ConcurrentHashMap(); + + final static Pattern p = Pattern.compile("aai_oxm_(.*).xml"); + + private static org.openecomp.cl.api.Logger logger = LoggerFactory.getInstance() + .getLogger(OxmModelLoader.class.getName()); + + public synchronized static void loadModels() throws CrudException { + ClassLoader cl = OxmModelLoader.class.getClassLoader(); + ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl); + Resource[] resources; + try { + resources = resolver.getResources("classpath*:/oxm/aai_oxm*.xml"); + } catch (IOException ex) { + logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, ex.getMessage()); + throw new CrudException("", Status.NOT_FOUND); + } + + if (resources.length == 0) { + logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, "No OXM schema files found on classpath"); + throw new CrudException("Failed to load schema", Status.NOT_FOUND); + } + + for (Resource resource : resources) { + Matcher matcher = p.matcher(resource.getFilename()); + + if (matcher.matches()) { + try { + OxmModelLoader.loadModel(matcher.group(1), resource); + } catch (Exception e) { + logger.error(CrudServiceMsgs.OXM_LOAD_ERROR, "Failed to load " + resource.getFilename() + + ": " + e.getMessage()); + throw new CrudException("Failed to load schema", Status.NOT_FOUND); + } + } + } + } + + private static void addtimer(String version, File file) { + TimerTask task = null; + task = new FileWatcher( + file) { + protected void onChange(File file) { + // here we implement the onChange + logger.info(CrudServiceMsgs.OXM_FILE_CHANGED, file.getName()); + + try { + OxmModelLoader.loadModel(version, file); + } catch (Exception e) { + e.printStackTrace(); + } + + } + }; + + if (!timers.containsKey(version)) { + Timer timer = new Timer("oxm-" + version); + timer.schedule(task, new Date(), 10000); + timers.put(version, timer); + + } + } + + private synchronized static void loadModel(String version, File file) + throws JAXBException, IOException { + InputStream inputStream = new FileInputStream(file); + loadModel(version, file.getName(), inputStream); + addtimer(version, file); + } + + private synchronized static void loadModel(String version, Resource resource) + throws JAXBException, IOException { + InputStream inputStream = resource.getInputStream(); + loadModel(version, resource.getFilename(), inputStream); + } + + private synchronized static void loadModel(String version, String resourceName, + InputStream inputStream) + throws JAXBException, IOException { + Map properties = new HashMap(); + properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, inputStream); + final DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory + .createContextFromOXM(Thread.currentThread().getContextClassLoader(), properties); + versionContextMap.put(version, jaxbContext); + logger.info(CrudServiceMsgs.LOADED_OXM_FILE, resourceName); + } + + public static DynamicJAXBContext getContextForVersion(String version) throws CrudException { + if (versionContextMap == null || versionContextMap.isEmpty()) { + loadModels(); + } else if (!versionContextMap.containsKey(version)) { + try { + loadModel(version, new File(CrudServiceConstants.CRD_HOME_MODEL + "aai_oxm_" + + version + ".xml")); + } catch (Exception e) { + throw new CrudException("", Status.NOT_FOUND); + } + } + + return versionContextMap.get(version); + } + + public static Map getVersionContextMap() { + return versionContextMap; + } + + public static void setVersionContextMap(Map versionContextMap) { + OxmModelLoader.versionContextMap = versionContextMap; + } + +} diff --git a/src/main/java/org/openecomp/schema/OxmModelValidator.java b/src/main/java/org/openecomp/schema/OxmModelValidator.java new file mode 100644 index 0000000..45a2597 --- /dev/null +++ b/src/main/java/org/openecomp/schema/OxmModelValidator.java @@ -0,0 +1,325 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.schema; + +import com.google.common.base.CaseFormat; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; + +import org.eclipse.persistence.dynamic.DynamicType; +import org.eclipse.persistence.internal.helper.DatabaseField; +import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; +import org.eclipse.persistence.mappings.DatabaseMapping; +import org.eclipse.persistence.oxm.XMLField; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.util.CrudServiceUtil; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.ws.rs.core.Response.Status; + +public class OxmModelValidator { + public enum Metadata { + NODE_TYPE("aai-node-type"), + URI("aai-uri"), + CREATED_TS("aai-created-ts"), + SOT("source-of-truth"), + LAST_MOD_SOT("last-mod-source-of-truth"); + + private final String propName; + + Metadata(String propName) { + this.propName = propName; + } + + public String propertyName() { + return propName; + } + + public static boolean isProperty(String property) { + for (Metadata meta : Metadata.values()) { + if (meta.propName.equals(property)) { + return true; + } + } + return false; + } + } + + + public static Map resolveCollectionfilter(String version, String type, + Map filter) + throws CrudException { + + DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version); + + Map result = new HashMap(); + if (jaxbContext == null) { + throw new CrudException("", Status.NOT_FOUND); + } + final DynamicType modelObjectType = jaxbContext.getDynamicType(CaseFormat.LOWER_CAMEL + .to(CaseFormat.UPPER_CAMEL, + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type))); + + for (String key : filter.keySet()) { + String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, key); + if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) != null) { + try { + DatabaseMapping mapping = modelObjectType.getDescriptor() + .getMappingForAttributeName(keyJavaName); + Object value = CrudServiceUtil.validateFieldType(filter.get(key), + mapping.getField().getType()); + result.put(key, value); + } catch (Exception ex) { + // Skip any exceptions thrown while validating the filter + // key value + continue; + } + } + } + + return result; + + } + + public static String resolveCollectionType(String version, String type) throws CrudException { + + DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version); + + if (jaxbContext == null) { + throw new CrudException("", Status.NOT_FOUND); + } + // Determine if the Object part is a collection type in the model + // definition + final DynamicType modelObjectType = jaxbContext.getDynamicType(CaseFormat.LOWER_CAMEL + .to(CaseFormat.UPPER_CAMEL, + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type))); + + if (modelObjectType == null) { + throw new CrudException("", Status.NOT_FOUND); + } + + if (modelObjectType.getDescriptor().getMappings().size() == 1 + && modelObjectType.getDescriptor().getMappings().get(0).isCollectionMapping()) { + String childJavaObjectName = modelObjectType.getDescriptor().getMappings() + .get(0).getAttributeName(); + childJavaObjectName = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, childJavaObjectName); + final DynamicType childObjectType = jaxbContext.getDynamicType(childJavaObjectName); + if (childObjectType == null) { + // Should not happen as child object is defined in oxm model + // itself + throw new CrudException("", Status.NOT_FOUND); + } + return childObjectType.getDescriptor().getTableName(); + } else { + return modelObjectType.getDescriptor().getTableName(); + } + + } + + + public static Vertex validateIncomingUpsertPayload(String id, String version, String type, + JsonElement properties) + throws CrudException { + + try { + type = resolveCollectionType(version, type); + DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version); + String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type)); + + final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass); + + Set> payloadEntriesSet = properties.getAsJsonObject() + .entrySet(); + + //loop through input to validate against schema + for (Map.Entry entry : payloadEntriesSet) { + String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, entry.getKey()); + + // check for valid field + if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) == null) { + throw new CrudException("Invalid field: " + entry.getKey(), Status.BAD_REQUEST); + } + + } + + Map entriesMap = new HashMap(); + for (Map.Entry entry : payloadEntriesSet) { + entriesMap.put(entry.getKey(), entry.getValue()); + } + + Vertex.Builder modelVertexBuilder = new Vertex.Builder(type); + if (id != null) { + modelVertexBuilder.id(id); + } + for (DatabaseMapping mapping : modelObjectType.getDescriptor().getMappings()) { + if (mapping.isAbstractDirectMapping()) { + DatabaseField field = mapping.getField(); + String defaultValue = mapping.getProperties().get("defaultValue") == null ? "" + : mapping.getProperties().get("defaultValue").toString(); + + String keyName = field.getName().substring(0, field.getName().indexOf("/")); + + if (((XMLField) field).isRequired() && !entriesMap.containsKey(keyName) + && !defaultValue.isEmpty()) { + modelVertexBuilder.property(keyName, + CrudServiceUtil.validateFieldType(defaultValue, field.getType())); + } + // if schema field is required and not set then reject + if (((XMLField) field).isRequired() && !entriesMap.containsKey(keyName) + && defaultValue.isEmpty()) { + throw new CrudException("Missing required field: " + keyName, Status.BAD_REQUEST); + } + // If invalid field then reject + if (entriesMap.containsKey(keyName)) { + Object value = CrudServiceUtil.validateFieldType(entriesMap.get(keyName) + .getAsString(), field.getType()); + modelVertexBuilder.property(keyName, value); + } + + // Set defaults + if (!defaultValue.isEmpty() && !entriesMap.containsKey(keyName)) { + modelVertexBuilder.property(keyName, + CrudServiceUtil.validateFieldType(defaultValue, field.getType())); + } + } + } + + return modelVertexBuilder.build(); + } catch (Exception e) { + throw new CrudException(e.getMessage(), Status.BAD_REQUEST); + } + } + + public static Vertex validateIncomingPatchPayload(String id, String version, String type, + JsonElement properties, Vertex existingVertex) + throws CrudException { + + try { + type = resolveCollectionType(version, type); + DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version); + String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, type)); + + final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass); + + Set> payloadEntriesSet = properties.getAsJsonObject() + .entrySet(); + + // Loop through the payload properties and merge with existing + // vertex props + for (Map.Entry entry : payloadEntriesSet) { + + String keyJavaName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, entry.getKey()); + + // check for valid field + if (modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) == null) { + throw new CrudException("Invalid field: " + entry.getKey(), Status.BAD_REQUEST); + } + + DatabaseField field = modelObjectType.getDescriptor() + .getMappingForAttributeName(keyJavaName).getField(); + String defaultValue = modelObjectType.getDescriptor() + .getMappingForAttributeName(keyJavaName) + .getProperties().get("defaultValue") == null ? "" + : modelObjectType.getDescriptor().getMappingForAttributeName(keyJavaName) + .getProperties().get("defaultValue").toString(); + + // check if mandatory field is not set to null + if (((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull + && !defaultValue.isEmpty()) { + existingVertex.getProperties().put(entry.getKey(), + CrudServiceUtil.validateFieldType(defaultValue, field.getType())); + } else if (((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull + && defaultValue.isEmpty()) { + throw new CrudException("Mandatory field: " + entry.getKey() + + " can't be set to null", + Status.BAD_REQUEST); + } else if (!((XMLField) field).isRequired() && entry.getValue() instanceof JsonNull + && existingVertex.getProperties().containsKey(entry.getKey())) { + existingVertex.getProperties().remove(entry.getKey()); + } else if (!(entry.getValue() instanceof JsonNull)) { + // add/update the value if found in existing vertex + Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(), + field.getType()); + existingVertex.getProperties().put(entry.getKey(), value); + } + + } + + return existingVertex; + } catch (Exception e) { + throw new CrudException(e.getMessage(), Status.BAD_REQUEST); + } + + } + + private static DatabaseField getDatabaseField(String fieldName, DynamicType modelObjectType) { + for (DatabaseField field : modelObjectType.getDescriptor().getAllFields()) { + int ix = field.getName().indexOf("/"); + if (ix <= 0) { + ix = field.getName().length(); + } + + String keyName = field.getName().substring(0, ix); + if (fieldName.equals(keyName)) { + return field; + } + } + return null; + } + + public static Vertex validateOutgoingPayload(String version, Vertex vertex) { + + Vertex.Builder modelVertexBuilder = new Vertex.Builder(vertex.getType()) + .id(vertex.getId().get()); + + try { + DynamicJAXBContext jaxbContext = OxmModelLoader.getContextForVersion(version); + String modelObjectClass = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, + vertex.getProperties().get(Metadata.NODE_TYPE.propertyName()) != null + ? vertex.getProperties().get(Metadata.NODE_TYPE.propertyName()).toString() : vertex.getType())); + final DynamicType modelObjectType = jaxbContext.getDynamicType(modelObjectClass); + + for (String key : vertex.getProperties().keySet()) { + DatabaseField field = getDatabaseField(key, modelObjectType); + if (field != null) { + if (!Metadata.isProperty(key)) { + modelVertexBuilder.property(key, vertex.getProperties().get(key)); + } + } + } + return modelVertexBuilder.build(); + } catch (Exception ex) { + return vertex; + } + + } + + +} diff --git a/src/main/java/org/openecomp/schema/RelationshipSchema.java b/src/main/java/org/openecomp/schema/RelationshipSchema.java new file mode 100644 index 0000000..5b28e28 --- /dev/null +++ b/src/main/java/org/openecomp/schema/RelationshipSchema.java @@ -0,0 +1,138 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.schema; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import org.openecomp.crud.exception.CrudException; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.ws.rs.core.Response.Status; + +public class RelationshipSchema { + private static final Gson gson = new GsonBuilder().create(); + + public static final String SCHEMA_SOURCE_NODE_TYPE = "source-node-type"; + public static final String SCHEMA_TARGET_NODE_TYPE = "target-node-type"; + public static final String SCHEMA_RELATIONSHIP_TYPE = "relationship-type"; + public static final String SCHEMA_RELATIONSHIP_TYPES_ARRAY = "relationship-types"; + public static final String SCHEMA_RELATIONSHIP_PROPERTIES = "properties"; + public static final String SCHEMA_RELATIONS_ARRAY = "relations"; + + /** + * key = source-node-type:target-node-type:relationship-type value = map of properties with name + * and type . Like propertyName:PropertyType + */ + private HashMap>> relations + = new HashMap>>(); + /** + * Hashmap of valid relationship types alongwith properrties. + */ + private HashMap>> relationTypes + = new HashMap>>(); + + + public RelationshipSchema(String json) throws CrudException { + + JsonParser parser = new JsonParser(); + try { + JsonObject root = parser.parse(json).getAsJsonObject(); + JsonArray relationshipTypesArray = root.getAsJsonArray(SCHEMA_RELATIONSHIP_TYPES_ARRAY); + JsonArray relationsArray = root.getAsJsonArray(SCHEMA_RELATIONS_ARRAY); + + //First load all the relationship-types + for (JsonElement item : relationshipTypesArray) { + JsonObject obj = item.getAsJsonObject(); + String type = obj.get(SCHEMA_RELATIONSHIP_TYPE).getAsString(); + + + HashMap> props = new HashMap>(); + Set> entries = obj.get(SCHEMA_RELATIONSHIP_PROPERTIES) + .getAsJsonObject().entrySet(); + + for (Map.Entry entry : entries) { + props.put(entry.getKey(), resolveClass(entry.getValue().getAsString())); + + } + relationTypes.put(type, props); + + } + + for (JsonElement item : relationsArray) { + JsonObject obj = item.getAsJsonObject(); + // Parse the Source/Taget nodeTypes + + String relationType = obj.get(SCHEMA_RELATIONSHIP_TYPE).getAsString(); + String key = obj.get(SCHEMA_SOURCE_NODE_TYPE).getAsString() + ":" + + obj.get(SCHEMA_TARGET_NODE_TYPE).getAsString() + ":" + relationType; + + + if (!relationTypes.containsKey(relationType)) { + throw new CrudException(SCHEMA_RELATIONSHIP_TYPE + ": " + relationType + " not found", + Status.BAD_REQUEST); + } + + relations.put(key, relationTypes.get(relationType)); + } + } catch (Exception e) { + throw new CrudException(e.getMessage(), Status.BAD_REQUEST); + } + + } + + + public HashMap> lookupRelation(String key) { + return this.relations.get(key); + } + + public HashMap> lookupRelationType(String type) { + return this.relationTypes.get(type); + } + + public boolean isValidType(String type) { + return relationTypes.containsKey(type); + } + + private Class resolveClass(String type) throws CrudException, ClassNotFoundException { + Class clazz = Class.forName(type); + validateClassTypes(clazz); + return clazz; + } + + private void validateClassTypes(Class clazz) throws CrudException { + if (!clazz.isAssignableFrom(Integer.class) && !clazz.isAssignableFrom(Double.class) + && !clazz.isAssignableFrom(Boolean.class) && !clazz.isAssignableFrom(String.class)) { + throw new CrudException("", Status.BAD_REQUEST); + } + } + + +} diff --git a/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java b/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java new file mode 100644 index 0000000..51b33d0 --- /dev/null +++ b/src/main/java/org/openecomp/schema/RelationshipSchemaLoader.java @@ -0,0 +1,158 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.schema; + +import org.apache.commons.io.IOUtils; +import org.openecomp.cl.eelf.LoggerFactory; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.logging.CrudServiceMsgs; +import org.openecomp.crud.util.CrudServiceConstants; +import org.openecomp.crud.util.FileWatcher; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedSet; +import java.util.Timer; +import java.util.TimerTask; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.ws.rs.core.Response.Status; +import javax.xml.bind.JAXBException; + + +public class RelationshipSchemaLoader { + + private static Map versionContextMap + = new ConcurrentHashMap(); + private static SortedSet versions = new TreeSet(); + private static Map timers = new ConcurrentHashMap(); + + final static Pattern filePattern = Pattern.compile("aai_relationship_(.*).json"); + + + private static org.openecomp.cl.api.Logger logger = LoggerFactory.getInstance() + .getLogger(RelationshipSchemaLoader.class.getName()); + + public synchronized static void loadModels() { + + File[] listOfFiles = new File(CrudServiceConstants.CRD_HOME_MODEL).listFiles(); + + if (listOfFiles != null) { + for (File file : listOfFiles) { + if (file.isFile()) { + Matcher matcher = filePattern.matcher(file.getName()); + if (matcher.matches()) { + try { + RelationshipSchemaLoader.loadModel(matcher.group(1), file); + } catch (Exception e) { + logger.error(CrudServiceMsgs.INVALID_OXM_FILE, file.getName(), e.getMessage()); + } + } + + } + } + } else { + logger.error(CrudServiceMsgs.INVALID_OXM_DIR, CrudServiceConstants.CRD_HOME_MODEL); + } + + + } + + private static void addtimer(String version, File file) { + TimerTask task = null; + task = new FileWatcher( + file) { + protected void onChange(File file) { + // here we implement the onChange + logger.info(CrudServiceMsgs.OXM_FILE_CHANGED, file.getName()); + + try { + RelationshipSchemaLoader.loadModel(version, file); + } catch (Exception e) { + e.printStackTrace(); + } + + } + }; + + if (!timers.containsKey(version)) { + Timer timer = new Timer("aai_relationship_" + version); + timer.schedule(task, new Date(), 10000); + timers.put(version, timer); + + } + } + + private synchronized static void loadModel(String version, File file) + throws JAXBException, IOException, CrudException { + + InputStream inputStream = new FileInputStream(file); + String content = IOUtils.toString(inputStream, "UTF-8"); + versionContextMap.put(version, new RelationshipSchema(content)); + addtimer(version, file); + versions.add(Integer.parseInt(version.substring(1))); + } + + public static RelationshipSchema getSchemaForVersion(String version) throws CrudException { + if (versionContextMap == null || versionContextMap.isEmpty()) { + loadModels(); + } else if (!versionContextMap.containsKey(version)) { + try { + loadModel(version, new File(CrudServiceConstants.CRD_HOME_MODEL + "aai_relationship_" + + version + ".json")); + } catch (Exception e) { + throw new CrudException("", Status.NOT_FOUND); + } + } + + return versionContextMap.get(version); + } + + public static String getLatestSchemaVersion() throws CrudException { + return "v" + versions.last(); + } + + public static Map getVersionContextMap() { + return versionContextMap; + } + + public static void setVersionContextMap(HashMap versionContextMap) { + RelationshipSchemaLoader.versionContextMap = versionContextMap; + } + + public static void main(String[] args) throws FileNotFoundException, Exception { + File initialFile = new File("C:\\Software\\gizmo\\src\\main\\java\\org\\openecomp\\schema\\vio.json"); + + loadModel("v8", initialFile); + } + +} diff --git a/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java b/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java new file mode 100644 index 0000000..552b60a --- /dev/null +++ b/src/main/java/org/openecomp/schema/RelationshipSchemaValidator.java @@ -0,0 +1,341 @@ +/** + * ============LICENSE_START======================================================= + * Gizmo + * ================================================================================ + * Copyright © 2017 AT&T Intellectual Property. + * Copyright © 2017 Amdocs + * 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========================================================= + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.openecomp.schema; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; + +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; +import org.openecomp.crud.service.EdgePayload; +import org.openecomp.crud.util.CrudServiceUtil; +import org.radeox.util.logging.Logger; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.ws.rs.core.Response.Status; + +public class RelationshipSchemaValidator { + + public static final String SOURCE_NODE = "source"; + public static final String TARGET_NODE = "target"; + + final static Pattern urlPattern = Pattern.compile("services/inventory/(.*)/(.*)/(.*)"); + + public static Map resolveCollectionfilter(String version, String type, + Map filter) + throws CrudException { + + RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version); + if (schema == null) { + throw new CrudException("", Status.NOT_FOUND); + } + + HashMap> props = schema.lookupRelationType(type); + Map result = new HashMap(); + + for (String key : filter.keySet()) { + + if (props.containsKey(key)) { + try { + Object value = CrudServiceUtil.validateFieldType(filter.get(key), props.get(key)); + result.put(key, value); + } catch (Exception ex) { + // Skip any exceptions thrown while validating the filter + // key value + continue; + } + } + } + + return result; + + } + + public static void validateType(String version, String type) throws CrudException { + + RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version); + if (!schema.isValidType(type)) { + throw new CrudException("Invalid " + RelationshipSchema.SCHEMA_RELATIONSHIP_TYPE + + ": " + type, + Status.BAD_REQUEST); + } + + } + + public static Edge validateIncomingAddPayload(String version, String type, Vertex sourceNode, + Vertex targetNode, JsonElement properties) + throws CrudException { + EdgePayload payload = new EdgePayload(); + payload.setSource("services/inventory/" + version + "/" + sourceNode.getType() + + "/" + sourceNode.getId().get()); + payload.setTarget("services/inventory/" + version + "/" + targetNode.getType() + + "/" + targetNode.getId().get()); + payload.setType(type); + payload.setProperties(properties); + return validateIncomingAddPayload(version, type, payload); + } + + public static Edge validateIncomingAddPayload(String version, String type, EdgePayload payload) + throws CrudException { + RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version); + + try { + + if (payload.getSource() == null || payload.getTarget() == null) { + throw new CrudException("Source/Target not specified", Status.BAD_REQUEST); + } + + Matcher sourceMatcher = urlPattern.matcher(payload.getSource()); + Matcher targetMatcher = urlPattern.matcher(payload.getTarget()); + + if (!sourceMatcher.matches() || !targetMatcher.matches()) { + throw new CrudException("Invalid Source/Target Urls", Status.BAD_REQUEST); + } + + // create key based on source:target:relationshipType + String sourceNodeType = sourceMatcher.group(2); + String targetNodeType = targetMatcher.group(2); + + String sourceNodeId = sourceMatcher.group(3); + String targetNodeId = targetMatcher.group(3); + + String key = sourceNodeType + ":" + targetNodeType + ":" + type; + + // find the validate the key from the schema + HashMap> schemaObject = schema.lookupRelation(key); + + if (schemaObject == null) { + throw new CrudException("Invalid source/target/relationship type: " + key, + Status.BAD_REQUEST); + } + + Edge.Builder modelEdgeBuilder = new Edge.Builder(type); + + modelEdgeBuilder.source(new Vertex.Builder(sourceNodeType).id(sourceNodeId).build()); + modelEdgeBuilder.target(new Vertex.Builder(targetNodeType).id(targetNodeId).build()); + + // validate it properties + validateEdgeProps(modelEdgeBuilder, payload.getProperties(), schemaObject); + + return modelEdgeBuilder.build(); + } catch (Exception ex) { + + throw new CrudException(ex.getMessage(), Status.BAD_REQUEST); + } + + } + + public static Edge validateIncomingPatchPayload(Edge edge, String version, EdgePayload payload) + throws CrudException { + RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version); + + try { + if (payload.getSource() != null) { + Matcher sourceMatcher = urlPattern.matcher(payload.getSource()); + + if (!sourceMatcher.matches()) { + throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST); + } + String sourceNodeId = sourceMatcher.group(3); + if (!sourceNodeId.equals(edge.getSource().getId().get())) { + throw new CrudException("Source can't be updated", Status.BAD_REQUEST); + } + } + + if (payload.getTarget() != null) { + Matcher targetMatcher = urlPattern.matcher(payload.getTarget()); + + if (!targetMatcher.matches()) { + throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST); + } + String sourceNodeId = targetMatcher.group(3); + if (!sourceNodeId.equals(edge.getTarget().getId().get())) { + throw new CrudException("Target can't be updated", Status.BAD_REQUEST); + } + } + // create key based on source:target:relationshipType + + String key = edge.getSource().getType() + ":" + edge.getTarget().getType() + + ":" + edge.getType(); + + // find the validate the key from the schema + HashMap> schemaObject = schema.lookupRelation(key); + + if (schemaObject == null) { + Logger.warn("key :" + key + + " not found in relationship schema . Skipping the schema validation"); + return edge; + } + + Set> entries = payload.getProperties() + .getAsJsonObject().entrySet(); + + for (Map.Entry entry : entries) { + + if (!schemaObject.containsKey(entry.getKey())) { + throw new CrudException("Invalid property: " + entry.getKey(), Status.BAD_REQUEST); + } else if (entry.getValue() instanceof JsonNull && edge.getProperties() + .containsKey(entry.getKey())) { + edge.getProperties().remove(entry.getKey()); + } else if (!(entry.getValue() instanceof JsonNull)) { + Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(), + schemaObject.get(entry.getKey())); + edge.getProperties().put(entry.getKey(), value); + } + + } + + return edge; + + } catch (Exception ex) { + + throw new CrudException(ex.getMessage(), Status.BAD_REQUEST); + } + } + + public static Edge validateIncomingUpdatePayload(Edge edge, String version, Vertex sourceNode, + Vertex targetNode, JsonElement properties) + throws CrudException { + EdgePayload payload = new EdgePayload(); + payload.setSource("services/inventory/" + version + "/" + sourceNode.getType() + + "/" + sourceNode.getId().get()); + payload.setTarget("services/inventory/" + version + "/" + targetNode.getType() + + "/" + targetNode.getId().get()); + payload.setType(edge.getType()); + payload.setProperties(properties); + return validateIncomingUpdatePayload(edge, version, payload); + } + + public static Edge validateIncomingUpdatePayload(Edge edge, String version, EdgePayload payload) + throws CrudException { + RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version); + + try { + + if (payload.getSource() != null) { + Matcher sourceMatcher = urlPattern.matcher(payload.getSource()); + + if (!sourceMatcher.matches()) { + throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST); + } + String sourceNodeId = sourceMatcher.group(3); + if (!sourceNodeId.equals(edge.getSource().getId().get())) { + throw new CrudException("Source can't be updated", Status.BAD_REQUEST); + } + } + + if (payload.getTarget() != null) { + Matcher targetMatcher = urlPattern.matcher(payload.getTarget()); + + if (!targetMatcher.matches()) { + throw new CrudException("Invalid Target Urls", Status.BAD_REQUEST); + } + String sourceNodeId = targetMatcher.group(3); + if (!sourceNodeId.equals(edge.getTarget().getId().get())) { + throw new CrudException("Target can't be updated", Status.BAD_REQUEST); + } + } + // create key based on source:target:relationshipType + + String key = edge.getSource().getType() + ":" + edge.getTarget().getType() + + ":" + edge.getType(); + + // find the validate the key from the schema + HashMap> schemaObject = schema.lookupRelation(key); + + if (schemaObject == null) { + Logger.warn("key :" + key + + " not found in relationship schema . Skipping the schema validation"); + return edge; + } + + Edge.Builder updatedEdgeBuilder = new Edge.Builder(edge.getType()).id(edge.getId().get()); + + updatedEdgeBuilder + .source(new Vertex.Builder(edge.getSource().getType()).id(edge.getSource().getId() + .get()).build()); + updatedEdgeBuilder + .target(new Vertex.Builder(edge.getTarget().getType()).id(edge.getTarget().getId() + .get()).build()); + + validateEdgeProps(updatedEdgeBuilder, payload.getProperties(), schemaObject); + + return updatedEdgeBuilder.build(); + } catch (Exception ex) { + + throw new CrudException(ex.getMessage(), Status.BAD_REQUEST); + } + } + + + private static void validateEdgeProps(Edge.Builder builder, JsonElement props, + HashMap> schemaObject) + throws CrudException { + Set> entries = props.getAsJsonObject().entrySet(); + + for (Map.Entry entry : entries) { + + if (!schemaObject.containsKey(entry.getKey())) { + throw new CrudException("Invalid property: " + entry.getKey(), Status.BAD_REQUEST); + } else { + Object value = CrudServiceUtil.validateFieldType(entry.getValue().getAsString(), + schemaObject.get(entry.getKey())); + builder.property(entry.getKey(), value); + } + + } + + } + + public static Edge validateOutgoingPayload(String version, Edge edge) throws CrudException { + + Edge.Builder modelEdgeBuilder = new Edge.Builder(edge.getType()).id(edge.getId() + .get()).source(edge.getSource()) + .target(edge.getTarget()); + + RelationshipSchema schema = RelationshipSchemaLoader.getSchemaForVersion(version); + + String key = edge.getSource().getType() + ":" + edge.getTarget().getType() + + ":" + edge.getType(); + HashMap> schemaObject = schema.lookupRelation(key); + + if (schemaObject == null || schemaObject.isEmpty()) { + return edge; + } + + for (String prop : edge.getProperties().keySet()) { + if (schemaObject.containsKey(prop)) { + modelEdgeBuilder.property(prop, edge.getProperties().get(prop)); + } + + } + return modelEdgeBuilder.build(); + } + +} diff --git a/src/main/resources/logging/CrudServiceMsgs.properties b/src/main/resources/logging/CrudServiceMsgs.properties new file mode 100644 index 0000000..850574f --- /dev/null +++ b/src/main/resources/logging/CrudServiceMsgs.properties @@ -0,0 +1,75 @@ +#Resource key=Error Code|Message text|Resolution text |Description text +####### +#Newlines can be utilized to add some clarity ensuring continuing line +#has atleast one leading space +#ResourceKey=\ +# CA0000E\ +# Sample error msg txt\ +# Sample resolution msg\ +# Sample description txt +# +###### +#Error code classification category +#000 Info/Debug +#100 Permission errors +#200 Availability errors/Timeouts +#300 Data errors +#400 Schema Interface type/validation errors +#500 Business process errors +#900 Unknown errors +# +######################################################################## + +PROCESS_REST_REQUEST=\ + CRD0001I|\ + Received request {0} {1} from {2}. Sending response: {3}|\ + None. Received the specified REST request from the source specified.|\ + Received the specified REST request from the source specified, and the CRUD service sent the specified response. + +INVALID_OXM_FILE=\ + CRD0002I|\ + Unable to parse schema file: {0} due to error : {1}\ + +OXM_FILE_CHANGED=\ + CRD0003I|\ + OXM file change detected: {0}\ + +STOPPING_CHAMP_DAO=\ + CRD0004I|\ + Stopping ChampDAO... + +INVALID_OXM_DIR=\ + CRD0005I|\ + Invalid OXM dir: {0}\ + +INSTANTIATE_GRAPH_DAO=\ + CRD0006I|\ + Instantiate data access layer for graph data store type: {0} graph: {1} using hosts: {2} + +LOADED_OXM_FILE=\ + CRD0007I|\ + Successfully loaded schema: {0} + +INVALID_GRAPH_BACKEND=\ + CRD0301E|\ + Unsupported graph database {0} specified. + +INSTANTIATE_GRAPH_BACKEND_ERR=\ + CRD0302E|\ + Failure instantiating {0} graph database backend. Cause: {1} + +EXCEPTION_DURING_METHOD_CALL=\ + CRD0502E|\ + Failed to {0} request for {1} due to: {2}|\ + Check error cause|\ + Method failed to execute + +OXM_LOAD_ERROR=\ + CRD0503E|\ + Unable to load OXM schema: {0} + + +TITAN_GRAPH_INFO=\ + CRD0504I|\ + Titan Graph Info: {0} + \ No newline at end of file diff --git a/src/main/runtime/context/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.context b/src/main/runtime/context/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.context new file mode 100644 index 0000000..8514196 --- /dev/null +++ b/src/main/runtime/context/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.context @@ -0,0 +1 @@ +{"context":{"contextClass":"ajsc.Context","contextId":"__module_ajsc_namespace_name__:__module_ajsc_namespace_version__","contextName":"__module_ajsc_namespace_name__","contextVersion":"__module_ajsc_namespace_version__","description":"__module_ajsc_namespace_name__ Context"}} \ No newline at end of file diff --git a/src/main/runtime/context/default#0.context b/src/main/runtime/context/default#0.context new file mode 100644 index 0000000..d1b5ab4 --- /dev/null +++ b/src/main/runtime/context/default#0.context @@ -0,0 +1 @@ +{"context":{"contextClass":"ajsc.Context","contextId":"default:0","contextName":"default","contextVersion":"0","description":"Default Context"}} \ No newline at end of file diff --git a/src/main/runtime/deploymentPackage/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.json b/src/main/runtime/deploymentPackage/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.json new file mode 100644 index 0000000..d0954cf --- /dev/null +++ b/src/main/runtime/deploymentPackage/__module.ajsc.namespace.name__#__module.ajsc.namespace.version__.json @@ -0,0 +1 @@ +{"deploymentPackage":{"Class":"ajsc.DeploymentPackage","Id":"__module.ajsc.namespace.name__:__module_ajsc_namespace_version__","namespace":"__module_ajsc_namespace_name__","namespaceVersion":"__module_ajsc_namespace_version__","description":"__module_ajsc_namespace_name__ __module_ajsc_namespace_version__ - default description","userId":"ajsc"}} \ No newline at end of file diff --git a/src/main/runtime/shiroRole/ajscadmin.json b/src/main/runtime/shiroRole/ajscadmin.json new file mode 100644 index 0000000..f5e981e --- /dev/null +++ b/src/main/runtime/shiroRole/ajscadmin.json @@ -0,0 +1 @@ +{"shiroRoleClass":"ajsc.auth.ShiroRole","shiroRoleId":"ajscadmin","name":"ajscadmin","permissions":"[ajscadmin:*, ajsc:*]"} \ No newline at end of file diff --git a/src/main/runtime/shiroRole/contextadmin#__module.ajsc.namespace.name__.json b/src/main/runtime/shiroRole/contextadmin#__module.ajsc.namespace.name__.json new file mode 100644 index 0000000..2dae9f5 --- /dev/null +++ b/src/main/runtime/shiroRole/contextadmin#__module.ajsc.namespace.name__.json @@ -0,0 +1 @@ +{"shiroRoleClass":"ajsc.auth.ShiroRole","shiroRoleId":"contextadmin:__module_ajsc_namespace_name__","name":"contextadmin:__module_ajsc_namespace_name__","permissions":"[]"} \ No newline at end of file diff --git a/src/main/runtime/shiroRole/contextadmin#default.json b/src/main/runtime/shiroRole/contextadmin#default.json new file mode 100644 index 0000000..5de814e --- /dev/null +++ b/src/main/runtime/shiroRole/contextadmin#default.json @@ -0,0 +1 @@ +{"shiroRoleClass":"ajsc.auth.ShiroRole","shiroRoleId":"contextadmin:default","name":"contextadmin:default","permissions":"[]"} \ No newline at end of file diff --git a/src/main/runtime/shiroUser/ajsc.json b/src/main/runtime/shiroUser/ajsc.json new file mode 100644 index 0000000..f4c7855 --- /dev/null +++ b/src/main/runtime/shiroUser/ajsc.json @@ -0,0 +1 @@ +{"shiroUserClass":"ajsc.auth.ShiroUser","shiroUserId":"ajsc","passwordHash":"9471697417008c880720ba54c6038791ad7e98f3b88136fe34f4d31a462dd27a","permissions":"[*:*]","username":"ajsc"} \ No newline at end of file diff --git a/src/main/runtime/shiroUserRole/ajsc#ajscadmin.json b/src/main/runtime/shiroUserRole/ajsc#ajscadmin.json new file mode 100644 index 0000000..cb8d483 --- /dev/null +++ b/src/main/runtime/shiroUserRole/ajsc#ajscadmin.json @@ -0,0 +1 @@ +{"shiroUserRoleClass":"ajsc.auth.ShiroUserRole","shiroUserRoleId":"ajsc:ajscadmin","roleId":"ajscadmin","userId":"ajsc"} \ No newline at end of file diff --git a/src/main/runtime/shiroUserRole/ajsc#contextadmin#__module.ajsc.namespace.name__.json b/src/main/runtime/shiroUserRole/ajsc#contextadmin#__module.ajsc.namespace.name__.json new file mode 100644 index 0000000..95d2361 --- /dev/null +++ b/src/main/runtime/shiroUserRole/ajsc#contextadmin#__module.ajsc.namespace.name__.json @@ -0,0 +1 @@ +{"shiroUserRoleClass":"ajsc.auth.ShiroUserRole","shiroUserRoleId":"ajsc:contextadmin:__module_ajsc_namespace_name__","roleId":"contextadmin:__module_ajsc_namespace_name__","userId":"ajsc"} \ No newline at end of file diff --git a/src/main/runtime/shiroUserRole/ajsc#contextadmin#default.json b/src/main/runtime/shiroUserRole/ajsc#contextadmin#default.json new file mode 100644 index 0000000..2bd5063 --- /dev/null +++ b/src/main/runtime/shiroUserRole/ajsc#contextadmin#default.json @@ -0,0 +1 @@ +{"shiroUserRoleClass":"ajsc.auth.ShiroUserRole","shiroUserRoleId":"ajsc:contextadmin:default","roleId":"contextadmin:default","userId":"ajsc"} \ No newline at end of file diff --git a/src/test/java/org/openecomp/crud/dao/champ/ChampDaoTest.java b/src/test/java/org/openecomp/crud/dao/champ/ChampDaoTest.java new file mode 100644 index 0000000..b11e274 --- /dev/null +++ b/src/test/java/org/openecomp/crud/dao/champ/ChampDaoTest.java @@ -0,0 +1,624 @@ +package org.openecomp.crud.dao.champ; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.openecomp.crud.dao.GraphDao; +import org.openecomp.crud.entity.Edge; +import org.openecomp.crud.entity.Vertex; +import org.openecomp.crud.exception.CrudException; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import static org.junit.Assert.*; + + +/** + * This suite of tests validates the basic functionality of the {@link ChampDao}. + */ +public class ChampDaoTest { + + private static final String GRAPH_NAME = "my_test_graph"; + + private GraphDao champDao = null; + + + /** + * Perform setup steps that must be done prior to executing each test. + */ + @Before + public void setup() { + + // Create an instance of the Champ DAO, backed by the Champ library's in-memory back end + // for testing purposes. + Properties champDaoProperties = new Properties(); + champDaoProperties.put(ChampDao.CONFIG_STORAGE_BACKEND, "in-memory"); + champDaoProperties.put(ChampDao.CONFIG_GRAPH_NAME, GRAPH_NAME); + champDao = new ChampDao(champDaoProperties); + } + + + /** + * Perform tear down steps that must be done after executing each test. + */ + @After + public void tearDown() { + + // Release the Champ DAO instance that we were using for the test. + if (champDao != null) { + ((ChampDao) champDao).close(); + } + } + + + /** + * Tests the ability of the {@link ChampDao} to create a vertex. + * + * @throws CrudException + */ + @Test + public void createVertexTest() throws CrudException { + + String VERTEX_TYPE = "Test_Vertex"; + + Map properties = new HashMap(); + properties.put("property1", "something"); + properties.put("property2", "something else"); + + // Create the vertex. + Vertex createdVertex = champDao.addVertex(VERTEX_TYPE, properties); + + // Validate that the returned {@link Vertex} has the right label assigned to it. + assertTrue("Unexpected vertex type '" + createdVertex.getType() + "' returned from DAO", + createdVertex.getType().equals(VERTEX_TYPE)); + + // Validate that all of the properties that we provided to the DAO are in fact assigned + // to the {@link Vertex} that we got back. + assertTrue("Vertex property list returned from DAO did not contain all expected properties - expected: " + + properties.keySet() + " actual: " + createdVertex.getProperties().keySet(), + createdVertex.getProperties().keySet().containsAll(properties.keySet())); + + // Validate that the values assigned to the properties in the returned {@link Vertex} + // match the ones that we provided. + for (String propertyKey : properties.keySet()) { + + assertTrue(createdVertex.getProperties().get(propertyKey).equals(properties.get(propertyKey))); + } + } + + + /** + * Tests the ability of the {@link ChampDao} to retrieve a vertex from the graph data store + * by its unique identifier. + * + * @throws CrudException + */ + @Test + public void getVertexByIdTest() throws CrudException { + + String VERTEX_TYPE = "Test_Vertex"; + + Map properties = new HashMap(); + properties.put("property1", "something"); + properties.put("property2", "something else"); + + // Create the vertex. + Vertex createdVertex = champDao.addVertex(VERTEX_TYPE, properties); + + // Make sure the {@link Vertex} returned from the create method includes an id that we can + // use to retrieve it. + assertTrue("No valid id returned for the created vertex", createdVertex.getId().isPresent()); + + // Now, retrieve the {@link Vertex} by its identifier. + Vertex retrievedVertex = champDao.getVertex(createdVertex.getId().get(), VERTEX_TYPE); + + // Validate that the retrieved {@link Vertex} has the right label assigned to it. + assertTrue("Unexpected vertex type '" + retrievedVertex.getType() + "' returned from DAO", + retrievedVertex.getType().equals(VERTEX_TYPE)); + + // Validate that all of the properties that we provided when we created the {@link Vertex} + // are present in the {@link Vertex} that we retrieved. + assertTrue("Vertex property list returned from DAO did not contain all expected properties - expected: " + + properties.keySet() + " actual: " + retrievedVertex.getProperties().keySet(), + retrievedVertex.getProperties().keySet().containsAll(properties.keySet())); + + // Validate that the values assigned to the properties in the retrieved {@link Vertex} + // match the ones that we provided when we created it. + for (String propertyKey : properties.keySet()) { + + assertTrue(retrievedVertex.getProperties().get(propertyKey).equals(properties.get(propertyKey))); + } + } + + + /** + * Tests the ability of the {@link ChampDao} to update an already existing vertex. + * + * @throws CrudException + */ + @Test + public void updateVertexTest() throws CrudException { + + final String VERTEX_TYPE = "Test_Vertex"; + + Map properties = new HashMap(); + properties.put("property1", "something"); + properties.put("property2", "something else"); + + // Create the vertex. + Vertex createdVertex = champDao.addVertex(VERTEX_TYPE, properties); + + // Make sure the {@link Vertex} returned from the create method includes an id that we can + // use to retrieve it. + assertTrue("No valid id returned for the created vertex", createdVertex.getId().isPresent()); + + // Modify the properties list... + properties.put("property3", "a new property"); + properties.remove("property1"); + + // ...and apply it to our vertex. + Vertex updatedVertex = champDao.updateVertex(createdVertex.getId().get(), createdVertex.getType(), properties); + + assertTrue("Vertex property list returned from DAO update operation did not contain all expected properties - expected: " + + properties.keySet() + " actual: " + updatedVertex.getProperties().keySet(), + updatedVertex.getProperties().keySet().containsAll(properties.keySet())); + + // Validate that the values assigned to the properties in the updated {@link Vertex} + // match the ones that we provided when we created it. + for (String propertyKey : properties.keySet()) { + + assertTrue("Unexpected value for property '" + propertyKey + "' - Expected: " + + properties.get(propertyKey) + " Actual: " + + updatedVertex.getProperties().get(propertyKey), + updatedVertex.getProperties().get(propertyKey).equals(properties.get(propertyKey))); + } + + // Validate that the property that we removed is NOT in the set of properties from our + // updated {@link Vertex}. + assertFalse("Property 'property1' should no longer be associated with updated vertex", + updatedVertex.getProperties().containsKey("property1")); + } + + + /** + * Tests the ability of the {@link ChampDao} to retrieve multiple vertices which match + * a particular set of supplied properties. + * + * @throws CrudException + */ + @Test + public void getVerticesTest() throws CrudException { + + final String FIRST_VERTEX_TYPE = "pserver"; + final String SECOND_VERTEX_TYPE = "complex"; + + // Create some vertices. + + Map vertex1Properties = new HashMap(); + vertex1Properties.put("O/S", "Linux"); + vertex1Properties.put("version", "6.5"); + vertex1Properties.put("hostname", "kll0001"); + champDao.addVertex(FIRST_VERTEX_TYPE, vertex1Properties); + + Map vertex2Properties = new HashMap(); + vertex2Properties.put("O/S", "Linux"); + vertex2Properties.put("version", "6.5"); + vertex2Properties.put("hostname", "kll0002"); + champDao.addVertex(FIRST_VERTEX_TYPE, vertex2Properties); + + Map vertex3Properties = new HashMap(); + vertex3Properties.put("O/S", "Linux"); + vertex3Properties.put("version", "7.2"); + vertex3Properties.put("hostname", "kll0003"); + champDao.addVertex(FIRST_VERTEX_TYPE, vertex3Properties); + + Map vertex4Properties = new HashMap(); + vertex4Properties.put("O/S", "Windows"); + vertex4Properties.put("version", "10"); + vertex4Properties.put("hostname", "Dev Laptop"); + champDao.addVertex(FIRST_VERTEX_TYPE, vertex4Properties); + + Map vertex5Properties = new HashMap(); + vertex5Properties.put("Street", "Baker"); + vertex5Properties.put("Number", "222B"); + champDao.addVertex(SECOND_VERTEX_TYPE, vertex5Properties); + + // Create a set of properties to use for our query. + Map queryProperties = new HashMap(); + queryProperties.put("O/S", "Linux"); + queryProperties.put("version", "6.5"); + + // Validate that we filter our 'get vertices' results by type + List allVerticesByType = champDao.getVertices(FIRST_VERTEX_TYPE, MapBuilder.builder().build()); + for (Vertex v : allVerticesByType) { + assertTrue("Unexpected vertex type returned from query. Expected: " + + FIRST_VERTEX_TYPE + " Actual: " + v.getType(), + v.getType().equals(FIRST_VERTEX_TYPE)); + } + + // Now, request the vertices that match our parameters. + List vertices = champDao.getVertices(FIRST_VERTEX_TYPE, queryProperties); + + // Validate that got back the expected number of vertices. + assertEquals(vertices.size(), 2); + + // Validate that the vertices we got back contain the expected parameters. + for (Vertex v : vertices) { + + assertTrue("Vertex from query result does not contain expected vertex 'O/S'", + v.getProperties().containsKey("O/S")); + assertTrue("Vertex from query result contains unexpected value for 'O/S' parameter - Expected: 'Linux' Actual: '" + + v.getProperties().get("O/S") + "'", + v.getProperties().get("O/S").equals("Linux")); + + assertTrue("Vertex from query result does not contain expected vertex 'O/S'", + v.getProperties().containsKey("version")); + assertTrue("Vertex from query result contains unexpected value for 'O/S' parameter - Expected: 'Linux' Actual: '" + + v.getProperties().get("O/S") + "'", + v.getProperties().get("version").equals("6.5")); + } + } + + @Test + public void deleteVertexTest() throws CrudException { + + boolean deletedVertexNotFound = false; + + // Create a vertex. + Vertex createdVertex = champDao.addVertex("test_type", MapBuilder.builder() + .withKeyValue("O/S", "Linux") + .withKeyValue("version", "6.5") + .withKeyValue("hostname", "kll0001") + .build()); + + // Verify that we can retrieve the vertex from the graph data base. + Vertex retrievedVertex = champDao.getVertex(createdVertex.getId().get(), "test_type"); + + // Now, delete the vertex. + champDao.deleteVertex(createdVertex.getId().get(), "test_type"); + + // Now, try to retrieve it again. This time we should fail to find it. + try { + champDao.getVertex(createdVertex.getId().get(), "test_type"); + + } catch (CrudException e) { + assertTrue(e.getMessage().contains("No vertex with id")); + deletedVertexNotFound = true; + } + + assertTrue("Should not have been able to retrieve deleted vertex", deletedVertexNotFound); + } + + @Test + public void createEdgeTest() throws CrudException { + + String EDGE_TYPE = "has"; + + // Create the source vertex for the edge. + Map srcVertexProperties = new HashMap(); + srcVertexProperties.put("O/S", "Linux"); + srcVertexProperties.put("version", "6.5"); + srcVertexProperties.put("hostname", "kll0001"); + Vertex sourceVertex = champDao.addVertex("vserver", srcVertexProperties); + + // Create the target vertex for the edge. + Map dstVertexProperties = new HashMap(); + dstVertexProperties.put("O/S", "Linux"); + dstVertexProperties.put("version", "6.5"); + dstVertexProperties.put("hostname", "kll0002"); + Vertex destVertex = champDao.addVertex("VNF", dstVertexProperties); + + // Now, create the edge itself. + Map edgeProperties = new HashMap(); + edgeProperties.put("prop", "val"); + Edge createdEdge = champDao.addEdge("has", sourceVertex, destVertex, edgeProperties); + + // Validate that the Edge object returned from the create method matches what we were + // trying to create. + assertTrue("Unexpected type for Edge returned from create method. Expected: " + EDGE_TYPE + + " Actual: " + createdEdge.getType(), + createdEdge.getType().equals("has")); + assertTrue("Unexpected properties for Edge returned from create method. Expected: " + edgeProperties + + " Actual: " + createdEdge.getProperties(), + createdEdge.getProperties().equals(edgeProperties)); + + } + + @Test + public void createEdgeWithMissingSrcOrTargetTest() throws CrudException { + + String EDGE_TYPE = "has"; + + // Create the source vertex for the edge. + Map srcVertexProperties = new HashMap(); + srcVertexProperties.put("O/S", "Linux"); + srcVertexProperties.put("version", "6.5"); + srcVertexProperties.put("hostname", "kll0001"); + Vertex sourceVertex = champDao.addVertex("vserver", srcVertexProperties); + + // Create the target vertex for the edge. + Map dstVertexProperties = new HashMap(); + dstVertexProperties.put("O/S", "Linux"); + dstVertexProperties.put("version", "6.5"); + dstVertexProperties.put("hostname", "kll0002"); + Vertex destVertex = champDao.addVertex("VNF", dstVertexProperties); + + // Now, try creating the Edge but specify an id for the source vertex that does + // not exist. + Map edgeProperties = new HashMap(); + edgeProperties.put("prop", "val"); + try { + champDao.addEdge(EDGE_TYPE, new Vertex.Builder("miss").id("99").build(), destVertex, edgeProperties); + } catch (CrudException e) { + assertTrue(e.getMessage().contains("Error creating edge - source vertex")); + } + + // Now, try created the Edge with a valid source vertex, but specify an id for the + // target vertex that does not exist. + try { + champDao.addEdge(EDGE_TYPE, sourceVertex, new Vertex.Builder("miss").id("99").build(), edgeProperties); + } catch (CrudException e) { + assertTrue(e.getMessage().contains("Error creating edge - target vertex")); + } + + } + + @Test + public void getEdgeByIdTest() throws CrudException { + + String EDGE_TYPE = "has"; + + // Create the source vertex for the edge. + Map srcVertexProperties = new HashMap(); + srcVertexProperties.put("O/S", "Linux"); + srcVertexProperties.put("version", "6.5"); + srcVertexProperties.put("hostname", "kll0001"); + Vertex sourceVertex = champDao.addVertex("vserver", srcVertexProperties); + + // Create the target vertex for the edge. + Map dstVertexProperties = new HashMap(); + dstVertexProperties.put("O/S", "Linux"); + dstVertexProperties.put("version", "6.5"); + dstVertexProperties.put("hostname", "kll0002"); + Vertex destVertex = champDao.addVertex("VNF", dstVertexProperties); + + // Now, create the edge itself. + Map edgeProperties = new HashMap(); + edgeProperties.put("prop", "val"); + Edge createdEdge = champDao.addEdge("has", sourceVertex, destVertex, edgeProperties); + + // Retrieve the edge we just created by specifying its unique identifier. + Edge retrievedEdge = champDao.getEdge(createdEdge.getId().get(), "has"); + + // Validate that the contents of the object that we got back matches what we thought we + // created. + assertTrue("Unexpected type for Edge returned from get method. Expected: " + EDGE_TYPE + + " Actual: " + retrievedEdge.getType(), + retrievedEdge.getType().equals(EDGE_TYPE)); + assertTrue("Unexpected properties for Edge returned from get method. Expected: " + edgeProperties + + " Actual: " + retrievedEdge.getProperties(), + retrievedEdge.getProperties().equals(edgeProperties)); + } + + @Test + public void getEdgesTest() throws CrudException { + + final String EDGE_TYPE_HAS = "has"; + final String EDGE_TYPE_RUNS = "runs"; + + // Create some vertices and edges that we can query agains. + Vertex complex = champDao.addVertex("complex", MapBuilder.builder() + .withKeyValue("Province", "Ontario") + .withKeyValue("City", "Ottawa") + .withKeyValue("Street", "303 Terry Fox") + .build()); + + Vertex vserver = champDao.addVertex("vserver", MapBuilder.builder() + .withKeyValue("O/S", "Linux") + .withKeyValue("version", "6.5") + .withKeyValue("hostname", "kll0001") + .build()); + + Vertex vnf1 = champDao.addVertex("vserver", MapBuilder.builder() + .withKeyValue("Application", "OpenDaylight") + .build()); + + Vertex vnf2 = champDao.addVertex("vserver", MapBuilder.builder() + .withKeyValue("Application", "Cammunda") + .build()); + + Edge edge1 = champDao.addEdge(EDGE_TYPE_HAS, complex, vserver, + MapBuilder.builder() + .withKeyValue("usesResource", "false") + .withKeyValue("hasDelTarget", "false") + .build()); + + Edge edge2 = champDao.addEdge(EDGE_TYPE_RUNS, vserver, vnf1, + MapBuilder.builder() + .withKeyValue("usesResource", "false") + .withKeyValue("hasDelTarget", "true") + .build()); + + Edge edge3 = champDao.addEdge(EDGE_TYPE_RUNS, vserver, vnf2, + MapBuilder.builder() + .withKeyValue("usesResource", "false") + .withKeyValue("hasDelTarget", "false") + .build()); + + // Query for all HAS edges. + List hasEdges = champDao.getEdges(EDGE_TYPE_HAS, new HashMap()); + + assertEquals("Unexpected number of edges of type 'has' found. Expected: 1 Actual: " + hasEdges.size(), + hasEdges.size(), 1); + assertTrue("Result of query for 'has' type edges does not contain the expected results", + containsEdge(edge1, hasEdges)); + + // Query for all RUNS edges. + List runsEdges = champDao.getEdges(EDGE_TYPE_RUNS, new HashMap()); + + assertEquals("Unexpected number of edges of type 'runs' found. Expected: 2 Actual: " + runsEdges.size(), + runsEdges.size(), 2); + assertTrue("Result of query for 'runs' type edges does not contain the expected results", + containsEdge(edge2, runsEdges)); + assertTrue("Result of query for 'runs' type edges does not contain the expected results", + containsEdge(edge2, runsEdges)); + + // Query for all HAS edges with the property 'hasDelTarget' equal to 'true'. + List runsEdgesWithDelTargetTrue = + champDao.getEdges(EDGE_TYPE_RUNS, MapBuilder.builder() + .withKeyValue("hasDelTarget", "true") + .build()); + + assertEquals("Unexpected number of edges of type 'has' with 'hasDelTarget=true' found. Expected: 1 Actual: " + + runsEdgesWithDelTargetTrue.size(), + runsEdgesWithDelTargetTrue.size(), 1); + assertTrue("Result of query for 'runs' type edges with delTarget set to TRUE does not contain the expected results", + containsEdge(edge2, runsEdgesWithDelTargetTrue)); + } + + @Test + @Ignore // For now - pending some expected fixes to the Champ library. + public void updateEdgeTest() throws CrudException { + + // Create the source vertex for the edge. + Vertex sourceVertex = champDao.addVertex("vserver", MapBuilder.builder() + .withKeyValue("O/S", "Linux") + .withKeyValue("version", "6.5") + .withKeyValue("hostname", "kll0001") + .build()); + + // Create the target vertex for the edge. + Vertex destVertex = champDao.addVertex("VNF", MapBuilder.builder() + .withKeyValue("O/S", "Linux") + .withKeyValue("version", "6.5") + .withKeyValue("hostname", "kll0002") + .build()); + + // Now, create the edge itself. + Edge createdEdge = champDao.addEdge("has", + sourceVertex, + destVertex, + MapBuilder.builder() + .withKeyValue("key1", "value1") + .withKeyValue("key2", "value2") + .withKeyValue("key3", "value3") + .build()); + + // Make sure the Edge returned from the create method includes an id that we can + // use to retrieve it. + assertTrue("No valid id returned for the created edge", createdEdge.getId().isPresent()); + + // Retrieve the properties map for our edge and make some changes. + Map properties = createdEdge.getProperties(); + properties.put("key4", "value4"); + properties.remove("key2"); + + // Now update the edge with the new properties map. + Edge updatedEdge = champDao.updateEdge(createdEdge); + + assertTrue("Edge property list returned from DAO update operation did not contain all expected properties - expected: " + + properties.keySet() + " actual: " + updatedEdge.getProperties().keySet(), + updatedEdge.getProperties().keySet().containsAll(properties.keySet())); + + // Validate that the values assigned to the properties in the updated Edge + // match the ones that we provided when we created it. + for (String propertyKey : properties.keySet()) { + + assertTrue("Unexpected value for property '" + propertyKey + "' - Expected: " + + properties.get(propertyKey) + " Actual: " + + updatedEdge.getProperties().get(propertyKey), + updatedEdge.getProperties().get(propertyKey).equals(properties.get(propertyKey))); + } + + // Validate that the property that we removed is NOT in the set of properties from our + // updated edge. + // *** We will leave this validation commented out for now, as the Champ library actually + // merges update properties instead of replacing them... +// assertFalse("Property 'key2' should no longer be associated with updated edge", +// updatedEdge.getProperties().containsKey("key2")); + } + + @Test + public void deleteEdgeTest() throws CrudException { + + boolean deletedEdgeNotFound = false; + + // Create the source vertex for the edge. + Vertex sourceVertex = champDao.addVertex("vserver", MapBuilder.builder() + .withKeyValue("O/S", "Linux") + .withKeyValue("version", "6.5") + .withKeyValue("hostname", "kll0001") + .build()); + + // Create the target vertex for the edge. + Vertex destVertex = champDao.addVertex("VNF", MapBuilder.builder() + .withKeyValue("O/S", "Linux") + .withKeyValue("version", "6.5") + .withKeyValue("hostname", "kll0002") + .build()); + + // Now, create the edge itself. + Edge createdEdge = champDao.addEdge("has", + sourceVertex, + destVertex, + MapBuilder.builder() + .withKeyValue("key1", "value1") + .withKeyValue("key2", "value2") + .withKeyValue("key3", "value3") + .build()); + + // Verify that we can retrieve the edge that we just created. + Edge retrievedEdge = champDao.getEdge(createdEdge.getId().get(), "has"); + + // Now, delete it. + champDao.deleteEdge(createdEdge.getId().get(), "has"); + + // Try retrieving it again. This time we should not find it. + try { + champDao.getEdge(createdEdge.getId().get(), "has"); + } catch (CrudException e) { + + assertTrue(e.getMessage().contains("No edge with id")); + deletedEdgeNotFound = true; + } + + assertTrue("Should not have been able to retrieve deleted edge.", deletedEdgeNotFound); + } + + private boolean containsEdge(Edge anEdge, List edges) { + + for (Edge e : edges) { + if (e.getId().isPresent() && anEdge.getId().isPresent() && (e.getId().get().equals(anEdge.getId().get()))) { + return true; + } + + } + return false; + } + + public static class MapBuilder { + + private Map map; + + private MapBuilder() { + map = new HashMap(); + } + + public static MapBuilder builder() { + return new MapBuilder(); + } + + public MapBuilder withKeyValue(String key, Object value) { + map.put(key, value); + return this; + } + + public Map build() { + return map; + } + } +} diff --git a/src/test/java/org/openecomp/schema/OxmModelLoaderTest.java b/src/test/java/org/openecomp/schema/OxmModelLoaderTest.java new file mode 100644 index 0000000..bf4d871 --- /dev/null +++ b/src/test/java/org/openecomp/schema/OxmModelLoaderTest.java @@ -0,0 +1,22 @@ +package org.openecomp.schema; + +import org.junit.Test; +import org.openecomp.crud.exception.CrudException; + +import static org.junit.Assert.assertTrue; + + +public class OxmModelLoaderTest { + + @Test + public void loadModels() { + try { + OxmModelLoader.loadModels(); + } catch (CrudException e) { + e.printStackTrace(); + assertTrue(false); + } + + assertTrue(OxmModelLoader.getVersionContextMap().size() == 4); + } +} diff --git a/src/test/resources/gremlin/gremlinResponseEmptyVertex.json b/src/test/resources/gremlin/gremlinResponseEmptyVertex.json new file mode 100644 index 0000000..0e4b9f6 --- /dev/null +++ b/src/test/resources/gremlin/gremlinResponseEmptyVertex.json @@ -0,0 +1,15 @@ +{ + "requestId": "54275f43-4cf5-491d-ad53-a9dada9c0fbf", + "status": { + "message": "", + "code": 200, + "attributes": { + } + }, + "result": { + "data": [ + ], + "meta": { + } + } +} \ No newline at end of file diff --git a/src/test/resources/gremlin/gremlinResponseMultipleVertex.json b/src/test/resources/gremlin/gremlinResponseMultipleVertex.json new file mode 100644 index 0000000..5809be3 --- /dev/null +++ b/src/test/resources/gremlin/gremlinResponseMultipleVertex.json @@ -0,0 +1,108 @@ +{ + "requestId":"54275f43-4cf5-491d-ad53-a9dada9c0fbf", + "status":{ + "message":"", + "code":200, + "attributes":{ + } + }, + "result":{ + "data":[ + { + "id":100, + "label":"vertex", + "type":"vertex", + "properties":{ + "aai-last-mod-ts":[ + { + "id":"j2mu-cwq8-sl", + "value":1477013842 + } + ], + "speed-value":[ + { + "id":"j312-cwq8-rnp", + "value":"50000000" + } + ], + "source-of-truth":[ + { + "id":"j5ee-cwq8-6bp", + "value":"SOT2" + } + ], + "aai-node-type":[ + { + "id":"j5sm-cwq8-8p1", + "value":"physical-link" + } + ] + } + }, + { + "id":200, + "label":"vertex", + "type":"vertex", + "properties":{ + "aai-last-mod-ts":[ + { + "id":"j2mu-cwq8-sl", + "value":1477013842 + } + ], + "speed-value":[ + { + "id":"j312-cwq8-rnp", + "value":"10000000" + } + ], + "source-of-truth":[ + { + "id":"j5ee-cwq8-6bp", + "value":"RCT" + } + ], + "aai-node-type":[ + { + "id":"j5sm-cwq8-8p1", + "value":"physical-link" + } + ] + } + }, + { + "id":300, + "label":"vertex", + "type":"vertex", + "properties":{ + "aai-last-mod-ts":[ + { + "id":"j2mu-cwq8-sl", + "value":1477013842 + } + ], + "speed-value":[ + { + "id":"j312-cwq8-rnp", + "value":"9999" + } + ], + "source-of-truth":[ + { + "id":"j5ee-cwq8-6bp", + "value":"RCT" + } + ], + "aai-node-type":[ + { + "id":"j5sm-cwq8-8p1", + "value":"physical-link" + } + ] + } + } + ], + "meta":{ + } + } +} \ No newline at end of file diff --git a/src/test/resources/gremlin/gremlinResponseVertex.json b/src/test/resources/gremlin/gremlinResponseVertex.json new file mode 100644 index 0000000..2e3a290 --- /dev/null +++ b/src/test/resources/gremlin/gremlinResponseVertex.json @@ -0,0 +1,76 @@ +{ + "requestId":"54275f43-4cf5-491d-ad53-a9dada9c0fbf", + "status":{ + "message":"", + "code":200, + "attributes":{ + } + }, + "result":{ + "data":[ + { + "id":602288, + "label":"vertex", + "type":"vertex", + "properties":{ + "aai-last-mod-ts":[ + { + "id":"j2mu-cwq8-sl", + "value":1477013842 + } + ], + "speed-value":[ + { + "id":"j312-cwq8-rnp", + "value":"10000000" + } + ], + "link-name":[ + { + "id":"j3fa-cwq8-u11", + "value":"ldouk116sd9:vmnic11-6/2|ldouktax102:xe-1/0/66" + } + ], + "resource-version":[ + { + "id":"j3ti-cwq8-1l1", + "value":"1477013842" + } + ], + "speed-units":[ + { + "id":"j47q-cwq8-t8l", + "value":"kbps" + } + ], + "aai-created-ts":[ + { + "id":"j4ly-cwq8-4qt", + "value":1476927322 + } + ], + "last-mod-source-of-truth":[ + { + "id":"j506-cwq8-5j9", + "value":"RCT" + } + ], + "source-of-truth":[ + { + "id":"j5ee-cwq8-6bp", + "value":"RCT" + } + ], + "aai-node-type":[ + { + "id":"j5sm-cwq8-8p1", + "value":"physical-link" + } + ] + } + } + ], + "meta":{ + } + } +} \ No newline at end of file diff --git a/version.properties b/version.properties new file mode 100644 index 0000000..325432a --- /dev/null +++ b/version.properties @@ -0,0 +1,13 @@ +# Versioning variables +# Note that these variables cannot be structured (e.g. : version.release or version.snapshot etc... ) +# because they are used in Jenkins, whose plug-in doesn't support + +major=1 +minor=1 +patch=0 + +base_version=${major}.${minor}.${patch} + +# Release must be completed with git revision # in Jenkins +release_version=${base_version} +snapshot_version=${base_version}-SNAPSHOT \ No newline at end of file -- 2.16.6