Skip to content

Commit 7c9569d

Browse files
committed
Wired up the 'trace' column checkboxes to the backend.
On page-load, the checks are populated according to the 'traced' property on the tree nodes. When the checkboxes are interacted with, the client sends the changes to the server, which updates the 'traced' state on the backend.
1 parent 68d5050 commit 7c9569d

File tree

6 files changed

+98
-10
lines changed

6 files changed

+98
-10
lines changed

codepulse/src/main/resources/toserve/pages/traces/PackageController.js

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,51 @@
3131

3232
var widgets = {}
3333

34+
function eligibleForWidget(node){
35+
if(!node) return false
36+
37+
return node.kind == 'package' ||
38+
node.kind == 'group' ||
39+
node.kind == 'root'
40+
}
41+
42+
// Get the partial selection state for the trace instrumentation flag, for nodes that
43+
// would exist in the package list. Note that <self> nodes will take their original
44+
// node's `id` and `traced` properties, and the original's `traced` will be set to
45+
// undefined. This function sets a `partialTraceSelection` value on nodes recursively,
46+
// with `1 = fully selected`, `0 = unselected`, and `undefined = partially selected`.
47+
(function calculatePartialTraceSelections(node){
48+
var isFullSelected = true,
49+
isPartialSelected = false
50+
51+
if(node.traced == 0) isFullSelected = false
52+
53+
node.children.forEach(function(kid){
54+
if(eligibleForWidget(kid)){
55+
var kidSelection = calculatePartialTraceSelections(kid)
56+
if(!kidSelection) isFullSelected = false
57+
if(kidSelection || kidSelection == undefined) isPartialSelected = true
58+
}
59+
})
60+
61+
var selectionValue = isFullSelected ? 1 : isPartialSelected ? undefined : 0
62+
node.partialTraceSelection = selectionValue
63+
return selectionValue
64+
})(treeData.root)
65+
66+
// special case: the root is never selected
67+
treeData.root.partialTraceSelection = 0
68+
3469
// initialize the `stateTemplate` and `widgets` maps
3570
// based on the package nodes in `treeData`
3671
;(function setupTreeHierarchy(packageParentNode, node){
37-
if(node.kind == 'package' || node.kind == 'group' || node.kind == 'root'){
72+
if(eligibleForWidget(node)){
3873

3974
var pw = new PackageWidget()
4075
widgets[node.id] = pw
4176

77+
pw.instrumentationSelected(node.partialTraceSelection)
78+
4279
pw.collapseChildren(/* collapsed = */true, /* animate = */false)
4380

4481
stateTemplate[node.id] = pw.selectedProp
@@ -60,6 +97,7 @@
6097
pw.uiParts.main.appendTo($totalsContainer)
6198
pw.abbreviatedLabel('Overall Coverage')
6299
pw.selectable(false)
100+
pw.instrumentationSelectable(false)
63101
}
64102

65103
if(node.kind == 'group' || node.kind == 'package'){

codepulse/src/main/resources/toserve/pages/traces/PackageWidget.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@
260260

261261
this.selectionClicks = new Bacon.Bus()
262262

263-
this.instrumentationSelectedProp = _instrumentationSelectedBus.toProperty(_instrumentationSelected).skipDuplicates()
263+
this.instrumentationSelectedProp = _instrumentationSelectedBus.toProperty(_instrumentationSelected).noLazy().skipDuplicates()
264264

265265
this.instrumentationSelectedClicks = new Bacon.Bus()
266266

codepulse/src/main/resources/toserve/pages/traces/TraceAPI.js

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
* was successful, `data` will be defined and `error` will be null; otherwise, `data` will be
5050
* null and `error` will be defined.
5151
*/
52-
function ajaxCommand(path, type, params, callback){
52+
function ajaxCommand(path, type, params, callback, opts){
5353
type = type || 'GET'
5454

5555
// allow for calling `ajaxCommand(path, type, callback)` i.e. no params
@@ -59,12 +59,14 @@
5959
}
6060
callback = callback || function(){}
6161

62-
$.ajax(commandPath(path), {
62+
var ajaxOpts = $.extend(opts || {}, {
6363
data: params,
6464
type: type,
6565
error: function(xhr, status){ callback(null, status) },
6666
success: function(data){ callback(data, null) }
6767
})
68+
69+
$.ajax(commandPath(path), ajaxOpts)
6870
}
6971

7072
/**
@@ -129,17 +131,21 @@
129131
}
130132

131133
/* Shortcut for calling `ajax` with a type of 'POST' */
132-
function postCommand(path, params, callback){
133-
ajaxCommand(path, 'POST', params, callback)
134+
function postCommand(path, params, callback, opts){
135+
ajaxCommand(path, 'POST', params, callback, opts)
136+
}
137+
138+
function putCommand(path, params, callback, opts){
139+
ajaxCommand(path, 'PUT', params, callback, opts)
134140
}
135141

136142
/* Shortcut for calling `ajax` with a type of 'GET' */
137-
function getCommand(path, params, callback){
138-
ajaxCommand(path, 'GET', params, callback)
143+
function getCommand(path, params, callback, opts){
144+
ajaxCommand(path, 'GET', params, callback, opts)
139145
}
140146

141-
function deleteCommand(path, params, callback){
142-
ajaxCommand(path, 'DELETE', params, callback)
147+
function deleteCommand(path, params, callback, opts){
148+
ajaxCommand(path, 'DELETE', params, callback, opts)
143149
}
144150

145151
function requestStart(){ postCommand('/start') }
@@ -164,6 +170,10 @@
164170
postCommand('/recording/' + id, {'color': color}, callback)
165171
},
166172

173+
'updateTreeInstrumentation': function(changesMap, callback){
174+
putCommand('/tree-instrumentation', JSON.stringify(changesMap), callback, {contentType: 'application/json'})
175+
},
176+
167177
'streamTraceCoverageCounts': function(loadParams, interval){
168178
return repeatingGetCommand({ url: commandPath('/coverage'), getData: loadParams, interval: interval })
169179
},

codepulse/src/main/resources/toserve/pages/traces/TreeData.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,11 @@
9898
kind: 'package',
9999
parent: node,
100100
children: [],
101+
traced: node.traced,
101102
isSelfNode: true
102103
}
103104
node.id += '_original'
105+
if(node.hasOwnProperty('traced')) node.traced = undefined
104106
selfNode.parentId = node.id
105107

106108
// update the tree.nodes mapping based on the id changes

codepulse/src/main/resources/toserve/pages/traces/traces.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,24 @@ $(document).ready(function(){
191191
Trace.treemapColoringStateChanges.push('Instrumented Packages Changed')
192192
})
193193

194+
controller.instrumentedWidgets.slidingWindow(2,2).onValue(function(ab){
195+
var before = ab[0], after = ab[1]
196+
function check(key){
197+
var oldV = before[key],
198+
newV = after[key]
199+
return oldV != newV
200+
}
201+
var changes = {}
202+
function checkKeys(obj){
203+
for(var key in obj){
204+
if(check(key)) changes[key] = after[key]
205+
}
206+
}
207+
checkKeys(before)
208+
checkKeys(after)
209+
TraceAPI.updateTreeInstrumentation(changes)
210+
})
211+
194212
/**
195213
* An Rx Property that represents the treemap's TreeData as it
196214
* changes due to the PackageWidgets' selection state.

codepulse/src/main/scala/com/secdec/codepulse/tracer/TraceAPIServer.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ class TraceAPIServer(manager: TraceManager) extends RestHelper with Loggable {
172172
/** /trace-api/<target.id>/treemap */
173173
val Treemap = simpleTargetPath("treemap")
174174

175+
val TreeInstrumentation = simpleTargetPath("tree-instrumentation")
176+
175177
/** /trace-api/<target.id>/recordings */
176178
val Recordings = simpleTargetPath("recordings")
177179

@@ -288,6 +290,24 @@ class TraceAPIServer(manager: TraceManager) extends RestHelper with Loggable {
288290
case Paths.Treemap(target) Get req =>
289291
TreemapDataStreamer.streamTreemapData(target.traceData)
290292

293+
case Paths.TreeInstrumentation(target) Put req =>
294+
def getBool(j: JValue) = j match {
295+
case JInt(num) => Some(num > 0)
296+
case JBool(b) => Some(b)
297+
case _ => None
298+
}
299+
300+
req.json.toOption match {
301+
case Some(json: JObject) => {
302+
for {
303+
JField(AsInt(key), rawValue) <- json.obj
304+
boolValue <- getBool(rawValue)
305+
} target.traceData.treeNodeData.updateTraced(key, boolValue)
306+
}
307+
case _ => BadResponse()
308+
}
309+
OkResponse()
310+
291311
// GET a JSON listing of all of the custom recordings for a trace.
292312
case Paths.Recordings(target) Get req =>
293313
JsonResponse(target.traceData.recordings.all.map(_.toJson))

0 commit comments

Comments
 (0)