aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
blob: 98e47f54a8ebcb29280ed2f803781eca265395d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
Champ
=====

What is Champ?
--------------

Champ is an abstraction from underlying graph storage systems that A&AI would otherwise interface with.

Building Champ
--------------

To build Champ run the following maven command from the project's root level pom directory:

    mvn clean install

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
    ../dynamic/conf
    
You will be mounting these as data volumes when you start the Docker container.

#### Configuring the Microservice

Create beans file **../dynamic/conf/champ-beans.xml**

Example:

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:util="http://www.springframework.org/schema/util"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/util
            http://www.springframework.org/schema/util/spring-util.xsd
            ">

        <util:constant id="DEFAULT_BATCH_SIZE" static-field="com.att.ecomp.event.client.DMaaPEventPublisher.DEFAULT_BATCH_SIZE" />
        <util:constant id="DEFAULT_BATCH_AGE" static-field="com.att.ecomp.event.client.DMaaPEventPublisher.DEFAULT_BATCH_AGE" />
        <util:constant id="DEFAULT_BATCH_DELAY" static-field="com.att.ecomp.event.client.DMaaPEventPublisher.DEFAULT_BATCH_DELAY" />

	
        <bean id="champEventPublisher" class="com.att.ecomp.event.client.DMaaPEventPublisher" >
            <constructor-arg name="host" value="<%= @CHAMP_EVBUS_HOSTS %>" />
            <constructor-arg name="topic" value="<%= @CHAMP_EVENT_TOPIC %>" />
            <constructor-arg name="username" value="<%= @CHAMP_DMAAP_API_KEY %>" />
            <constructor-arg name="password" value="<%= @CHAMP_DMAAP_API_SECRET %>" />
            <constructor-arg name="maxBatchSize" ref="DEFAULT_BATCH_SIZE" />
            <constructor-arg name="maxAgeMs" ref="DEFAULT_BATCH_AGE" />
            <constructor-arg name="delayBetweenBatchesMs" ref="DEFAULT_BATCH_DELAY" />
            <constructor-arg name="transportType" value="HTTPAUTH" />		
        </bean>
    
        <!-- Graph Implementation Configuration-->
        <util:map id="props" map-class="java.util.HashMap" key-type="java.lang.String" value-type="java.lang.Object">
            <entry key="champcore.event.stream.buffer.capacity" value="50" value-type="java.lang.Integer"/>
            <entry key="champcore.event.stream.publisher-pool-size" value="10" value-type="java.lang.Integer"/>
            <entry key="champcore.event.stream.publisher" value-ref="champEventPublisher"/>
    
            <entry key="graph.name" value="<%= @CHAMP_GRAPH_NAME %>"/>
            <entry key="storage.backend" value="<%= @CHAMP_STORAGE_BACKEND_DB %>"/>
            <entry key="storage.hostname" value="<%= @CHAMP_GRAPH_HOST %>"/>
        
            <!-- Hbase Config -->
            <entry key="storage.hbase.ext.hbase.zookeeper.property.clientPort" value="<%= @CHAMP_GRAPH_PORT %>"/>
            <entry key="storage.hbase.ext.zookeeper.znode.parent" value="/hbase-unsecure"/>
        
            <!-- Cassandra Config -->
            <entry key="storage.port" value="<%= @CHAMP_GRAPH_PORT %>"/>
        </util:map>
    
        <!-- Janus Implementation -->
        <bean id="graphBuilder" class="org.onap.aai.champjanus.graph.impl.JanusChampGraphImpl$Builder">
            <constructor-arg value="<%= @CHAMP_GRAPH_NAME %>"/>
            <constructor-arg ref="props" />
        </bean>
        <bean id="graphImpl" class="org.onap.aai.champjanus.graph.impl.JanusChampGraphImpl">
            <constructor-arg ref="graphBuilder" />
        </bean>
        <bean id="champRestService" class="org.onap.champ.ChampRESTAPI" >
            <constructor-arg ref="graphImpl" />
		    <constructor-arg name="txTimeOutInSec" value="300" />
        </bean>
    </beans>

Create configuration file **../appconfig/auth/champ-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 CHAMP 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/champ-service/config/ \
		-e KEY_STORE_PASSWORD={{obfuscated password}} \
		-e KEY_MANAGER_PASSWORD=OBF:{{obfuscated password}} \
	    -v /<path>/logs:/opt/aai/logroot/AAI-CHAMP \
	    -v /<path>/appconfig:/opt/app/champ-service/config \
	    --name champ-service \
	    {{your docker repo}}/champ-service

Where,

    {{your docker repo}} = The Docker repository you have published your CHAMP Service image to.
    {{obfuscated password}} = The password for your key store/key manager after running it through the Jetty obfuscation tool.

API Specification
-----------------

Champ has methods for:

1) Objects
2) Relationships
3) Transactions

In the future we plan on adding in partitions, indicies, schemas, and traversals, but at the moment that is outside the scope of Champ.  If you have suggestions on how this should be implemented, we look forward to your pull request.

API Implementations
-------------------

Champ ships with both Titan and Janus implementations. These are switchable after deployment, but the champ-beans.xml file needs to be changed and the champ-service must be restarted.

Event Generation
----------------

Champ can be configured to generate a notification whenever it is used to modify data in the graph.  The notification comes in the form of an event which is posted to the **champRawEvents** Event Bus topic.  This event stream allows downstream clients to be notified about objects and relationships which are added/modified/deleted in the graph.

The following configuration parameters define the behaviour of the event generation feature:

- **champ.event.stream.publisher**: _EventClientPublisher_ instance to use for forwarding events to the event stream (see below).
- **champ.event.stream.publisher-pool-size**: Optional: number of worker threads to use for event publishing.
- **champ.event.stream.buffer.capacity**: Optional: maximum number of events which may be enqueued waiting to be published at any given time.
 

The following examples illustrate snippets of typical spring-beans configuration file which instantiate a producer (if your client is not spring enabled then you may just directly instantiate an _EventBusPublisher_ - refer to the _ECOMP Event Bus Client_ library for details):

_Instantiating an event producer backed by a native Kafka implementation:_

    <!-- Event publisher to pass to the Champ library for logging raw graph events. -->
    <bean id="champEventPublisher" class="com.att.ecomp.event.client.KafkaEventPublisher" >
        <constructor-arg name="hosts" value="1.2.3.4:9092, 5.6.7.8:9092, 9.10.11.12:9092" />
        <constructor-arg name="topic" value="champRawEvents" />
    </bean>

_Instantiating an event producer backed by a DMaaP Client implementation:_

    <bean id="champEventPublisher" class="com.att.ecomp.event.client.DMaaPEventPublisher" >
        <constructor-arg name="urlList" value="1.2.3.4, 5.6.7.8, 9.10.11.12" />
        <constructor-arg name="topic" value="champRawEvents" />
        <constructor-arg name="apiKey" value="OBF:1r2v1qc51i0r1l6n1m4q1jew1bpt1lkp1ll11bot1jee1m7o1l6n1i0z1qax1r53" />
        <constructor-arg name="apiSecret" value="OBF:1ro81caa1myq1mzs1nx31kfl1d2q1zsp1yt81nz31f8h1hmd1hmx1fa51nxb1yte1zt11d3g1kct1nzb1mxi1myk1cb01rqe" />
    </bean>

	

Usage
-----

### Echo

    URL: https://<host>:9522/services/echo-service/echo/<input>
    Method: GET
    Success Response: 200 OK


### Objects

#### Create a new object
Inserts a new object into the graph with the type and properties from the body of the request. Returns the object that was created, along with its assigned key and timestamps.

    URL: https://<host>:9522/services/champ-service/v1/objects
    Method: POST
    Body: 
         {
           "type" : "test",
           "properties" : {
             "key1" : "val1",
             "key2" : "val2"
           }
         }
         
    Success Response:
        Code: 201
        Content:
            {
                "key": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                "type": "test",
                "properties": {
                    "key1": "val1",
                    "key2": "val2",
                    "aai-uuid": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                    "aai-created-ts": 1516731449014,
                    "aai-last-mod-ts": 1516731449014
                }
            }

#### Retrieve an object
A GET request that returns the object with the given key

    URL: https://<host>:9522/services/champ-service/v1/objects/<key>
    Method: GET
    Success Response:
        Code: 200 OK
        Content:
            {
                "key": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                "type": "test",
                "properties": {
                    "key1": "val1",
                    "key2": "val2",
                    "aai-uuid": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                    "aai-created-ts": 1516731449014,
                    "aai-last-mod-ts": 1516731449014
                }
            }

#### Updating an object
Replace any of the properties with a PUT request, get the updated object back. Inclusion of timestamps is optional, but the request will be rejected if they do not match the DB.

    URL: https://<host>:9522/services/champ-service/v1/objects/<key>
    Method: PUT
    Content:
        {
            "key": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
            "type": "test",
            "properties": {
                "key1": "val3",
                "key2": "val2",
                "aai-uuid": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                "key4": "val4",
                "aai-created-ts": 1516731449014
            }
        }
        
    Response:
        Code: 200 OK
        Content:
            {
                "key": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                "type": "test",
                "properties": {
                    "key1": "val3",
                    "key2": "val2",
                    "aai-uuid": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                    "key4": "val4",
                    "aai-created-ts": 1516731449014,
                    "aai-last-mod-ts": 1516730919213
                }
            }

#### Delete objects
Deletes the object from the graph if there are no connected relationships

    URL: https://<host>:9522/services/champ-service/v1/objects/<key>
    Method: DELETE
    Success Response:
        Code: 200 OK
        

  
#### Filtered object search
Get a list of objects filtered by key/value pairs

    URL: https://<host>:9522/services/champ-service/v1/objects/filter?<key>=<val>
    Method: GET
    Success Response:
        [
            {
                "key": "e0d3a253-4a1b-4ca4-a862-8a52b1e3fdfb",
                "type": "test2",
                "properties": {}
            }
        ]

Get a list of objects filtered by key/value pairs with specific properties

    URL: https://<host>:9522/services/champ-service/v1/objects/filter?<key>=<val>&properties=<prop1>&properties=<prop2>
    Method: GET
    Success Response:
        [
            {
                "key": "e0d3a253-4a1b-4ca4-a862-8a52b1e3fdfb",
                "type": "test2",
                "properties": {
                    "key1": "val1",
                    "filter-sample": "yes"
                }
            }
        ] 

Get a list of objects filtered by key/value pairs with all properties

    URL: https://<host>:9522/services/champ-service/v1/objects/filter?<key>=<val>&properties=all
    Method: GET
    Success Response:
        [
            {
                "key": "e0d3a253-4a1b-4ca4-a862-8a52b1e3fdfb",
                "type": "test2",
                "properties": {
                    "key1": "val1",
                    "aai-uuid": "e0d3a253-4a1b-4ca4-a862-8a52b1e3fdfb",
                    "filter-sample": "yes"
                }
            }
        ]

### Relationships
Relationships are used to create a connection between two pre-existing objects.

#### Create a new relationship
Creates a new relationship with the specified properties. "source" and "target" must be objects that have already been created, specified by their keys. Returns the created relationship with its key and timestamps.

    URL: https://<host>:9522/services/champ-service/v1/relationships
    Method: POST
    Content:
        {
            "type": "testOnTest2",
            "properties": {
                "beep": "boop",
                "a": "b"
            },
            "source": {
                "key": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                "type": "test",
                "properties": {
                    "key1": "val3",
                    "key2": "val2",
                    "aai-uuid": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                    "key4": "val4"
                }
            },
            "target": {
                "key": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                "type": "test2",
                "properties": {
                    "key1": "val1",
                    "key2": "val2",
                    "aai-uuid": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                    "key4": "val4"
                }
            }
        }
        
    Response:
        Code: 201 Created
        Content:
        {
            "key": "7a3282d0-6904-40f2-ae1e-8246bb1f49c1",
            "type": "testOnTest2",
            "properties": {
                "beep": "boop",
                "a": "b",
                "aai-uuid": "7a3282d0-6904-40f2-ae1e-8246bb1f49c1"
                "aai-last-mod-ts": 1516730919213,
                "aai-created-ts": 1516730919213
            },
            "source": {
                "key": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                "type": "test",
                "properties": {
                    "key1": "val3",
                    "key2": "val2",
                    "aai-uuid": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                    "key4": "val4"
                }
            },
            "target": {
                "key": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                "type": "test2",
                "properties": {
                    "key1": "val1",
                    "key2": "val2",
                    "aai-uuid": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                    "key4": "val4"
                }
            }
        }
        
#### Retrieving relationships
Returns the relationship, looked up by key.

    URL: https://<host>:9522/services/champ-service/v1/relationships/<key>
    Method: GET
    Response:
        Code: 200 OK
        Content:
            {
                "key": "7a3282d0-6904-40f2-ae1e-8246bb1f49c1",
                "type": "testOnTest2",
                "properties": {
                    "beep": "boop",
                    "a": "b",
                    "aai-uuid": "7a3282d0-6904-40f2-ae1e-8246bb1f49c1"
                    "aai-last-mod-ts": 1516730919213,
                    "aai-created-ts": 1516730919213
                },
                "source": {
                    "key": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                    "type": "test",
                    "properties": {
                        "key1": "val3",
                        "key2": "val2",
                        "aai-uuid": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                        "key4": "val4"
                    }
                },
                "target": {
                    "key": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                    "type": "test2",
                    "properties": {
                        "key1": "val1",
                        "key2": "val2",
                        "aai-uuid": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                        "key4": "val4"
                    }
                }
            }
            
            #### Get relationships for an object
            Given an object, returns all connected relationships.
            
                URL: https://<host>:9522/services/champ-service/v1/objects/relationships/<object-id>
                Method: GET
                Success Response:
                    Code: 200 OK
                    Content: 
                        [
                            {
                                "key": "4ba8dcc2-806d-4312-aecb-503435f355e5",
                                "type": "testOnTest2",
                                "properties": {
                                    "beep": "fdsa",
                                    "a": "c",
                                    "aai-uuid": "4ba8dcc2-806d-4312-aecb-503435f355e5"
                                },
                                "source": {
                                    "key": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                                    "type": "test",
                                    "properties": {
                                        "key1": "val3",
                                        "key2": "val2",
                                        "aai-uuid": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                                        "key4": "val4"
                                    }
                                },
                                "target": {
                                    "key": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                                    "type": "test2",
                                    "properties": {
                                        "key1": "val1",
                                        "key2": "val2",
                                        "aai-uuid": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                                        "key4": "val4"
                                    }
                                }
                            },
                            {
                                "key": "a3096bb8-dc66-4a9c-ab33-a1183f784fbb",
                                "type": "testOnTest2",
                                "properties": {
                                    "beep": "fdsa",
                                    "a": "c",
                                    "aai-uuid": "a3096bb8-dc66-4a9c-ab33-a1183f784fbb"
                                },
                                "source": {
                                    "key": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                                    "type": "test",
                                    "properties": {
                                        "key1": "val3",
                                        "key2": "val2",
                                        "aai-uuid": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                                        "key4": "val4"
                                    }
                                },
                                "target": {
                                    "key": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                                    "type": "test2",
                                    "properties": {
                                        "key1": "val1",
                                        "key2": "val2",
                                        "aai-uuid": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                                        "key4": "val4"
                                    }
                                }
                            }
                        ]

#### Updating relationships
Update the relationship properties. Passing timestamps is optional, but the request will be rejected if they are incorrect.

    URL: https://<host>:9522/services/champ-service/v1/relationships/<key>
    Method: PUT
    Content: 
        {
            "key": "7a3282d0-6904-40f2-ae1e-8246bb1f49c1",
            "type": "testOnTest2",
            "properties": {
                "beep": "borp",
                "a": "c",
                "aai-uuid": "7a3282d0-6904-40f2-ae1e-8246bb1f49c1"
            },
            "source": {
                "key": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                "type": "test",
                "properties": {
                    "key1": "val3",
                    "key2": "val2",
                    "aai-uuid": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                    "key4": "val4"
                }
            },
            "target": {
                "key": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                "type": "test2",
                "properties": {
                    "key1": "val1",
                    "key2": "val2",
                    "aai-uuid": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                    "key4": "val4"
                }
            }
        }
        
    Response:
        Code: 200 OK
        Content:
            {
                "key": "7a3282d0-6904-40f2-ae1e-8246bb1f49c1",
                "type": "testOnTest2",
                "properties": {
                    "beep": "borp",
                    "a": "c",
                    "aai-uuid": "7a3282d0-6904-40f2-ae1e-8246bb1f49c1",
                    "aai-last-mod-ts": 1516734987294,
                    "aai-created-ts": 1516730919213
                },
                "source": {
                    "key": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                    "type": "test",
                    "properties": {
                        "key1": "val3",
                        "key2": "val2",
                        "aai-uuid": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                        "key4": "val4"
                    }
                },
                "target": {
                    "key": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                    "type": "test2",
                    "properties": {
                        "key1": "val1",
                        "key2": "val2",
                        "aai-uuid": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                        "key4": "val4"
                    }
                }
            }
            
#### Deleting relationships
Deletes the relationship specified by key.

    URL: https://<host>:9522/services/champ-service/v1/relationships/<key>
    Method: Delete
    Response: 200 OK
    
#### Filtered Relationship
Returns a list of relationships that have key/value properties matching the filter

    URL: https://<host>:9522/services/champ-service/v1/objects/filter?<key>=<val>
    Method: GET
    Success Response:
        [
            {
                "key": "a4d51cd9-f271-4201-975d-168ec6bde501",
                "type": "testOnTest2",
                "properties": {
                    "beep": "yes",
                    "a": "c",
                    "aai-uuid": "a4d51cd9-f271-4201-975d-168ec6bde501"
                },
                "source": {
                    "key": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                    "type": "test",
                    "properties": {
                        "key1": "val3",
                        "key2": "val2",
                        "aai-uuid": "890c8b3f-892f-48e3-85cd-748ebf0426a5",
                        "key4": "val4"
                    }
                },
                "target": {
                    "key": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                    "type": "test2",
                    "properties": {
                        "key1": "val1",
                        "key2": "val2",
                        "aai-uuid": "559855df-62e2-4b06-a1ae-18e0d5ac9826",
                        "key4": "val4"
                    }
                }
            }
        ]

### Transactions
Transactions allow multiple graph operations to be grouped into a logical, sandboxed context, so that the option exists to either persist ALL of the grouped changes together, or NONE of them.

Explicit use of transactions is entirely optional for the client.  Calling the API methods described below without supplying a transaction
object results in a transaction being implicitly opened for the single operation, and then automatically committed.

However, all of the API calls described above related to persisting, retrieving, and deleting vertices, edges and graph partitions also 
expose a version of the call which optionally accepts a transaction id (acquired by explicitly calling the /transaction API endpoint).
In this case, the supplied transaction is used for the operation, and no automatic commit occurs.  It is the responsibility of
the client to explicitly commit or rollback the transaction at his or her discretion.

#### Open a new transaction
To use explicit transaction the client must request a transaction id from the Champ service by making a request to open a new transaction.

	URL: https://<host>:9522/services/champ-service/v1/transaction/
	Method: POST
	Response:
	    Code: 200 OK
	    Content: 5d90f5ae-1f1e-4c3e-b20b-2f7c45f822eb
	    
#### Working within a transaction
Operations can be done within a transaction by putting the transactionId in the query string.

    Query string: transactionId=<id>
    
Example object creation:

    URL: https://<host>:9522/services/champ-service/v1/objects?transactionId=5d90f5ae-1f1e-4c3e-b20b-2f7c45f822eb
    
Example relationship update:

    URL: https://<host>:9522/services/champ-service/v1/relationships/<key>?transactionId=5d90f5ae-1f1e-4c3e-b20b-2f7c45f822eb

#### Checking a transaction
If you wish to check the status of a transaction, you can do a get on it

    URL: https://<host>:9522/services/champ-service/v1/transaction/<transaction-id>
    Method: GET
If the transaction is currently open:

    Response:
        Code: 200 OK
        Content: "fa0890d9-6ac4-40aa-9838-745a25a61fa6 is OPEN"
If the transaction is not open:

    Response:
        Code: 404 Not Found
        Content: {}
    

#### Committing a transaction
Operations performed within the context of a transaction are not visible outside of that context 
until the client explicitly commits the transaction.  Up until that point, there is always the
option to just roll back the changes.

    URL: https://<host>:9522/services/champ-service/v1/transaction/<transaction-id>
    Method: PUT
    Content: {"method":"commit"}
    
    Response:
        Code: 200 OK
        Content: COMMITED
    
### Rolling back a transaction.
In the event that a sequence of graph operations which were performed within the same transactional context need to be aborted (for example one of the operations in the sequence encountered a failure of some kind) the entire transaction can be aborted by rolling back the transaction.  This effectively undoes all of the operations which were performed within the open transaction.

Note, once a transaction has been committed, it is no longer possible to rollback the contents of the transaction.
 
    URL: https://<host>:9522/services/champ-service/v1/transaction/<transaction-id>
        Method: PUT
        Content: {"method":"rollback"}
        
        Response:
            Code: 200 OK
            Content: ROLLED BACK