Creating graphs from json output of /stats ?

Hi,

Can anybody share their technique explaining how you created your own graphs using the data from Apigee's "/stats" api?

I'm a bit embarrassed to admit I started out doing an ultra low tech method of copy/paste from the JSON response into excel. Technically it works, but it could become a full time job if I send the wrong person a report they love and then they want it every day.

I'm hoping somebody on here has a much smoother method that they can share?

Thanks!

Mark

0 5 1,177
5 REPLIES 5

Gah! Cut/paste?!?!! No, don't do that.

There are lots of options of course.

Most recently the option I have used, that I liked, was to run a nodejs Script that pulls /stats data, and then pushes that data into a Google Spreadsheet (via the API of course!). The sheets API also allows the client to insert formulas, charts, and so on. So you'd have all the raw data, plus the chart. See https://github.com/DinoChiesa/ApigeeEdge-API-Traffic-Summarizer

I have also used d3, the JavaScript framework to visualize results.

I'll flag a couple of my colleagues to see if they have any good examples to share.

@Keith Danekind, do you have anything in your bag of tricks?

@Cass Obregon, what about you?

Yes, as @Dino-at-Google wrote, there are a lot of great options! I like Google Charts (https://developers.google.com/chart/) for creating nice looking graphs on a web page.

You can create a simple line graph with a little Javascript and a call to the stats APIs:

7393-graph.jpeg

Here is the code for a single page web application that draws that graph. You can save the code to an HTML file and run it in Chrome. It will request your Apigee user name and password so it can get an OAuth token for the call to stats API. Once it has a token it will ask for a org name and environment, for example "myorg" and "test". You can supply a date range of a few days to see how it works. This is a quick example that is not robust or extensively tested.

// Sample code, offered as is without any support


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Analytics API Query Example</title>
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>    
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
	<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
	<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>






    <script type="text/javascript">
	    
	    // OAuth token for Apigee Edge Management APIs
	    var token = "";


		// Load the Visualization API 
		google.load('visualization', '1.0', {'packages':['corechart']});
    	    	
		// wait until the document loads
		$( document ).ready(function() {


			// if the query button was clicked
			$("#queryButton").click(getData);
			
			// if the login button was clicked
			$("#loginButton").click(getToken);
	    });
	    
	    // Get an access token for Apigee Edge managment APIs
	    function getToken() {
		    
		    // get values from input fields
		    var username = $('#username').val();
		    var password = $('#password').val();
		    var mfa = $('#mfa').val();		    		    
		    
		    console.log("Getting token...");
		    
			// Call Apigee Edge managment authentication API
			jQuery.ajax({
			    url: "https://login.apigee.com/oauth/token",
			    type: "POST",
			    headers: {
			        "Authorization": "Basic ZWRnZWNsaTplZGdlY2xpc2VjcmV0",
			        "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
			    },
			    contentType: "application/x-www-form-urlencoded",
			    data: {
			        "username": username,
			        "password": password,
			        "mfa_token": mfa,
			        "grant_type": "password",
			    },
			})


			.done(function(data, textStatus, jqXHR) {
			    console.log("Login succeeded: " + jqXHR.status);
				token = data.access_token;
				$('#loginStatus').html("");
				$('#auth').hide();
				$('#application').show();
				
				$('#password').val('');
				$('#mfa').val('');
			})
			.fail(function(jqXHR, textStatus, errorThrown) {
			    console.log("HTTP Request Failed");
			    if(jqXHR.status == 401) {
					$('#loginStatus').html("Invalid username/password");
				}
				else {
					$('#loginStatus').html("Error (" + jqXHR.status + "): " + errorThrown);
				}
			})
			.always(function() {
			    /* ... */
			});


	    }
	        
	    // Call Edge management API for analtyics and get data
	    function getData() {
			// read values from HTML input fields
			var org = $('#org').val();
			var environment = $('#environment').val();
			var user = $('#user').val();
			var password = $('#password').val();
			var startDate = $('#datepickerStart').val();
			var endDate = $('#datepickerEnd').val();


			// update status, ui
			$('#chart_div').hide();
			$('#statusMessage').html("Running query...");
			$('#statusMessage').show();
			console.log("Running query...");
			
			// call management API and get analytics data
			jQuery.ajax({
			    url: "https://api.enterprise.apigee.com/v1/o/" + org + "/environments/" + environment + "/stats/apps/",
			    type: "GET",
			    data: {
			        "select": "sum(message_count)",
			        "timeRange": startDate + " 06:00~" + endDate + " 07:00",    // adjust for your timezone offset from GMT
			        "timeUnit": "hour",
			    },
			    headers: {
			        "Authorization": "Bearer " + token
			    },
			})
			.done(function(data, textStatus, jqXHR) {
				$('#statusMessage').hide();
				console.log("Query successful");	
			    updateData(data);
			})
			.fail(function(jqXHR, textStatus, errorThrown) {
			    console.log("HTTP Request Failed");
			    $('#statusMessage').html(textStatus + ": " + errorThrown);
				$('#responsecode').html("Status: " + jqXHR.status);
				
				// if "unauthorized", show login UI
				if(jqXHR.status == 401) {
					onTokenExpiration();	
				}
			});	    
	    }
	
		// update HTML, draw graph
        function updateData(myData) {   
	        if(myData.environments[0].dimensions[0].metrics[0].values.length == 0) {
	        	// no results? tell the user
	        	$('#dateInfo').html("No data");
	        	console.log("No data");
				$('#chart_div').hide();	
	        	return;
	        }
	        
	       	// new variable, for convenience, to shorten the path
		    var counts = myData.environments[0].dimensions[0].metrics[0].values;
		    
		    // sort data by timestamp
		    counts.sort(function(a, b) {return a.timestamp - b.timestamp} );
		    	        
			// create the data structure for the graph
			var data = new google.visualization.DataTable();
						
			data.addColumn('date', 'Time');
			data.addColumn('number', 'Requests');			
			
            // Build data for graph
            for(var i=0; i < counts.length; i++) {
				temp = [];
				temp.push(new Date(counts[i].timestamp)); // convert timestamp string to a Date
				temp.push(Number(counts[i].value));			
	            data.addRow(temp);
            }	
            
			// Change format of tool-tip to show date and hours and minutes
			var date_formatter = new google.visualization.DateFormat({ pattern: "MMM dd, yyyy HH:mm"}); 
			date_formatter.format(data, 0);
			
			// Set chart options
			var options = {
				chartArea: { top: 10, left: 70 },
				width: 1200, 
				height: 600,
				backgroundColor: '#1e1e1e',
				vAxis: {
				  textStyle: { color: '#fff'},
				  titleTextStyle: { color: '#fff'}
				},
				hAxis: {
				  textStyle: { color: '#fff'},
				  titleTextStyle: { color: '#fff'},
				  baselineColor: { color: '#fff'}
				},				
				legend: {
				  textStyle: { color: '#fff'},
				  titleTextStyle: { color: '#fff'},
				},
				animation: {
				  startup: true, 
				  duration: 500,
				  easing: 'out'
				},
				series: {
				  0: { 
					  color: '#88C1F2'
				  }


				}	
			};
			
			// Instantiate and draw chart, passing in chart options.
			var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
			$('#chart_div').show();
			chart.draw(data, options);      
        }


		// set up UI for start and end date picker
		$(function() {
			$( "#datepickerStart" ).datepicker({ dateFormat: 'mm/dd/yy' });
			$( "#datepickerEnd" ).datepicker({ dateFormat: 'mm/dd/yy' });
  		});


    </script>
</head>


<body style="color: white; font-family: sans-serif; background-color: #1e1e1e">


<!-- login section -->
<div id="auth" style="border: 1px solid #858585">


	<p>
	<div style="font-family: sans-serif; font-size: 20px; font-weight: 400; color: white;"> Please log into Apigee Edge</div>
	<table border="0" cellpadding="10">
		<tr>
			<td align="right">Username</td>
			<td><input type="text" value="" id="username"></td>
			<td></td>
		</tr>
		<tr>
			<td align="right">Password</td>
			<td><input type="password" value="" id="password"></td>
			<td><input type="submit" value="Log in" id="loginButton"></td>
		</tr>
		<tr>
			<td align="right">MFA token<br/><span style="font-size: x-small">(if required)</span></td>
			<td><input type="password" value="" id="mfa"></td>
			<td></td>
		</tr>
	</table>
	</p>
	<div id="loginStatus"></div>
</div>
		
<!-- application section -->
<div id="application" style="display: none;">
	<p>Org: <input type="text" value="org name here" id="org">
	   Environment: <input type="text" value="prod" id="environment"></p>
	   <hr/>
	<br/>
	<p>Start date: <input type="text" id="datepickerStart" value="08/01/2018">   
	   End date: <input type="text" id="datepickerEnd" value="08/15/2018">   
	   <input type="submit" value="Run query" id="queryButton">
	</p>
	<div style="font-family: sans-serif; font-size: 40px; font-weight: 400; color: white;">Requests Per Hour</div>
	<div id="statusMessage" style="font-family: sans-serif; font-size: 30px; font-weight: 300; color: white"></div><div>
	<!-- div for the graph -->
	<div id="chart_div"></div>
	<hr>
	<div id="responsecode"></div>		
</div>
</body>
</html>


Thank you very much! I will give this a try. My companies "edge" environment is secured with SSO so I think I'll change it to just accept a token that I get using a passcode and the "get_token" utility but this is a huge help!

@Mark Hammelman

Here is a simplified version that assumes you already have a token. I removed all of the token retrieval code and UI and replaced it with an input field for a token.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Analytics API Query Example</title>
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>    
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
	<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
	<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>


    <script type="text/javascript">
	    
		// Load the Visualization API 
		google.load('visualization', '1.0', {'packages':['corechart']});
    	    	
		// wait until the document loads
		$( document ).ready(function() {


			// if the query button was clicked
			$("#queryButton").click(getData);
	    });
	    	        
	    // Call Edge management API for analtyics and get data
	    function getData() {
			// read values from HTML input fields
			var org = $('#org').val();
			var environment = $('#environment').val();
			var token = $('#token').val();
			var startDate = $('#datepickerStart').val();
			var endDate = $('#datepickerEnd').val();


			// update status, ui
			$('#chart_div').hide();
			$('#statusMessage').html("Running query...");
			$('#statusMessage').show();
			console.log("Running query...");
			
			// call management API and get analytics data
			jQuery.ajax({
			    url: "https://api.enterprise.apigee.com/v1/o/" + org + "/environments/" + environment + "/stats/apps/",
			    type: "GET",
			    data: {
			        "select": "sum(message_count)",
			        "timeRange": startDate + " 06:00~" + endDate + " 07:00",    // adjust for your timezone offset from GMT
			        "timeUnit": "hour",
			    },
			    headers: {
			        "Authorization": "Bearer " + token
			    },
			})
			.done(function(data, textStatus, jqXHR) {
				$('#statusMessage').hide();
				console.log("Query successful");	
			    updateData(data);
			})
			.fail(function(jqXHR, textStatus, errorThrown) {
			    console.log("HTTP Request Failed");
			    $('#statusMessage').html(textStatus + ": " + errorThrown);
				$('#responsecode').html("Status: " + jqXHR.status);
				
				// if "unauthorized", show login UI
				if(jqXHR.status == 401) {
					onTokenExpiration();	
				}
			});	    
	    }
	
		// update HTML, draw graph
        function updateData(myData) {   
	        if(myData.environments[0].dimensions[0].metrics[0].values.length == 0) {
	        	// no results? tell the user
	        	$('#dateInfo').html("No data");
	        	console.log("No data");
				$('#chart_div').hide();	
	        	return;
	        }
	        
	       	// new variable, for convenience, to shorten the path
		    var counts = myData.environments[0].dimensions[0].metrics[0].values;
		    
		    // sort data by timestamp
		    counts.sort(function(a, b) {return a.timestamp - b.timestamp} );
		    	        
			// create the data structure for the graph
			var data = new google.visualization.DataTable();
						
			data.addColumn('date', 'Time');
			data.addColumn('number', 'Requests');			
			
            // Build data for graph
            for(var i=0; i < counts.length; i++) {
				temp = [];
				temp.push(new Date(counts[i].timestamp)); // convert timestamp string to a Date
				temp.push(Number(counts[i].value));			
	            data.addRow(temp);
            }	
            
			// Change format of tool-tip to show date and hours and minutes
			var date_formatter = new google.visualization.DateFormat({ pattern: "MMM dd, yyyy HH:mm"}); 
			date_formatter.format(data, 0);
			
			// Set chart options
			var options = {
				chartArea: { top: 10, left: 70 },
				width: 1200, 
				height: 600,
				backgroundColor: '#1e1e1e',
				vAxis: {
				  textStyle: { color: '#fff'},
				  titleTextStyle: { color: '#fff'}
				},
				hAxis: {
				  textStyle: { color: '#fff'},
				  titleTextStyle: { color: '#fff'},
				  baselineColor: { color: '#fff'}
				},				
				legend: {
				  textStyle: { color: '#fff'},
				  titleTextStyle: { color: '#fff'},
				},
				animation: {
				  startup: true, 
				  duration: 500,
				  easing: 'out'
				},
				series: {
				  0: { 
					  color: '#88C1F2'
				  }


				}	
			};
			
			// Instantiate and draw chart, passing in chart options.
			var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
			$('#chart_div').show();
			chart.draw(data, options);      
        }


		// set up UI for start and end date picker
		$(function() {
			$( "#datepickerStart" ).datepicker({ dateFormat: 'mm/dd/yy' });
			$( "#datepickerEnd" ).datepicker({ dateFormat: 'mm/dd/yy' });
  		});


    </script>
</head>


<body style="color: white; font-family: sans-serif; background-color: #1e1e1e">
		
<!-- application section -->
<div id="application" >
	<p>Org: <input type="text" value="org name here" id="org">
	   Environment: <input type="text" value="prod" id="environment">
	   Token: <input type="password" value="token" id="token"></p>
	   <hr/>
	<br/>
	<p>Start date: <input type="text" id="datepickerStart" value="08/01/2018">   
	   End date: <input type="text" id="datepickerEnd" value="08/15/2018">   
	   <input type="submit" value="Run query" id="queryButton">
	</p>
	<div style="font-family: sans-serif; font-size: 40px; font-weight: 400; color: white;">Requests Per Hour</div>
	<div id="statusMessage" style="font-family: sans-serif; font-size: 30px; font-weight: 300; color: white"></div><div>
	<!-- div for the graph -->
	<div id="chart_div"></div>
	<hr>
	<div id="responsecode"></div>		
</div>
</body>
</html>

Excellent, Can I embed this solution into the developer portal ?

with filtering for developer specific data