Skip to content

Commit

Permalink
Updated Query model to support future query types
Browse files Browse the repository at this point in the history
  • Loading branch information
dustinlarimer committed Apr 13, 2014
1 parent 27f4f35 commit 4b629cb
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 198 deletions.
9 changes: 4 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ function KeenApi(config) {
request.get(self.readKey, path, params, callback);
}
};
_.extend(this, KeenQuery.client);

this.run = KeenQuery.client.run;
}

function configure(config) {
Expand Down Expand Up @@ -224,7 +224,6 @@ function decryptScopedKey(apiKey, scopedKey) {
module.exports = {
configure: configure,
encryptScopedKey: encryptScopedKey,
decryptScopedKey: decryptScopedKey
decryptScopedKey: decryptScopedKey,
Query: KeenQuery.Query
};

_.extend(module.exports, KeenQuery.analyses);
125 changes: 46 additions & 79 deletions lib/query.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var _ = require('underscore');
var KeenRequests = require('./requests');

/*!
/*!
* -----------------
* Keen IO Query JS
* -----------------
Expand All @@ -10,134 +10,101 @@ var KeenRequests = require('./requests');
var Keen = {};

// ------------------------------
// Keen.Query
// Keen.Request
// ------------------------------

Keen.Query = function(){
Keen.Request = function(){
this.data = {};
this.configure.apply(this, arguments);
}

Keen.Query.prototype.configure = function(client, analyses, callback){
Keen.Request.prototype.configure = function(client, queries, callback){
this.client = client;
this.analyses = analyses;
this.queries = queries;
this.callback = callback;
this.refresh();
this.run();
return this;
};

Keen.Query.prototype.refresh = function(){
var self = this,
completions = 0,
Keen.Request.prototype.run = function(){
var self = this,
completions = 0,
response = [];

var handleResponse = function(err, res){
if (err && self.callback) {
return self.callback(err, null);
}
response[arguments[2]] = res, completions++;
if (completions == self.analyses.length) {
self.data = (self.analyses.length == 1) ? response[0] : response;
if (completions == self.queries.length) {
self.data = (self.queries.length == 1) ? response[0] : response;
if (self.callback) self.callback(null, self.data);
}
};
_.each(self.analyses, function(analysis, index){

_.each(self.queries, function(query, index){
var data, path = '/projects/' + self.client.projectId;
var callbackSequencer = function(err, res){
handleResponse(err, res, index);
};

if (analysis instanceof Keen.Analysis) {
path += analysis.path;
data = analysis.params || {};

}

if (query instanceof Keen.Query) {
path += query.path;
data = query.params || {};
}
/* TODO: Test and deploy this
else if (_.isString(analysis)) {
path += '/saved_queries/' + analysis + '/result';
else if (_.isString(query)) {
path += '/saved_queries/' + query + '/result';
data = { api_key: self.client.readKey };
}*/
}*/
else {
throw new Error('Analysis #' + (index+1) +' is not valid');
throw new Error('Query #' + (index+1) +' is not valid');

}

KeenRequests.get.call(self.client, self.client.readKey, path, data, callbackSequencer);
});

return self;
};

// Export <client>.query method
// ------------------------------
module.exports.client = {
query: function(input, callback){
if (!input) throw new Error('Queries require at least one analysis');
var analyses = (_.isArray(input)) ? input : [input];
return new Keen.Query(this, analyses, callback);
}
return self;
};


// ------------------------------
// Keen.Analysis
// Keen.Query
// ------------------------------

Keen.Analysis = function(){};
Keen.Query = function(){
this.configure.apply(this, arguments);
};

Keen.Analysis.prototype.configure = function(resource, collection, params){
if (!collection) throw new Error('Event Collection name is required');
this.path = '/queries/' + resource + '?event_collection=' + collection;
this.event_collection = collection;
Keen.Query.prototype.configure = function(analysisType, params){
//if (!collection) throw new Error('Event Collection name is required');
this.path = '/queries/' + analysisType;
this.params = params || {};
return this;
};

Keen.Analysis.prototype.get = function(attribute) {
Keen.Query.prototype.get = function(attribute) {
return this.params[attribute] || null;
};

Keen.Analysis.prototype.set = function(attributes) {
Keen.Query.prototype.set = function(attributes) {
for (var attribute in attributes) {
this.params[attribute] = attributes[attribute];
}
return this;
};

// Analysis Types
// ------------------------------
var analysisTypes = [
'Count',
'Count_Unique',
'Sum',
'Average',
'Minimum',
'Maximum',
'Select_Unique',
'Extraction',
'Funnel'
];

// Build and export methods
// ------------------------------
module.exports.analyses = {};

_.each(analysisTypes, function(type){
Keen.Analysis[type] = function(){
var args = Array.prototype.slice.call(arguments);
this.configure.apply(this, [type.toLowerCase()].concat(args));
};
Keen.Analysis[type].prototype = new Keen.Analysis();
module.exports.analyses[type.replace("_","")] = Keen.Analysis[type];
});

// Funnels are special
// Export Methods
// ------------------------------
Keen.Analysis.Funnel.prototype.configure = function(resource, params) {
if (!params.steps) throw new Error('Funnel steps are required');
this.path = '/queries/' + resource;
this.params = params || {};
return this;
}
module.exports = {
client: {
run: function(query, callback){
if (!query) throw new Error('At least one query is required');
var queries = (_.isArray(query)) ? query : [query];
return new Keen.Request(this, queries, callback);
}
},
Query: Keen.Query
};
21 changes: 17 additions & 4 deletions lib/requests.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
var rest = require('superagent');
var crypto = require('crypto');
var qs = require('querystring');

// Handle logic of processing response, including error messages
// The error handling should be strengthened over time to be more
// The error handling should be strengthened over time to be more
// meaningful and robust
// ---------------------------------------------------------------

Expand All @@ -20,12 +19,26 @@ function processResponse(err, res, callback) {
return callback(null, res.body);
}

function buildQueryString(params){
var query = [];
for (var key in params) {
if (params[key]) {
var value = params[key];
if (Object.prototype.toString.call(value) !== '[object String]') {
value = JSON.stringify(value);
}
value = encodeURIComponent(value);
query.push(key + '=' + value);
}
}
return "?" + query.join('&');
};

module.exports = {
get: function(apiKey, path, data, callback) {
rest
.get(this.baseUrl + this.apiVersion + path)
.get(this.baseUrl + this.apiVersion + path + buildQueryString(data))
.set('Authorization', apiKey)
.query(data || {})
.end(function(err, res) {
processResponse(err, res, callback);
});
Expand Down
Loading

0 comments on commit 4b629cb

Please sign in to comment.