Skip to content

Instantly share code, notes, and snippets.

@samdelagarza
Created June 11, 2013 22:26
Show Gist options
  • Save samdelagarza/5761308 to your computer and use it in GitHub Desktop.
Save samdelagarza/5761308 to your computer and use it in GitHub Desktop.
Simple oData builder
define(['underscore', 'moment'], function (_, moment) {
var Url = function (uriString) {
var queryHash = {},
url = uriString,
buildQueryParameter = function (name, value) {
// http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type -- application/x-www-form-urlencoded
// don't append "=" for null values, according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#url-parameter-serialization
return encodeURI(name) + (value !== null ? "=" + encodeURI(value) : "");
};
/**
* builds the url string from params and the uri set in the ~ctor
* @returns {string}
*/
this.buildQuery = function () {
// according to http://tools.ietf.org/html/rfc3986 or http://labs.apache.org/webarch/uri/rfc/rfc3986.html
// being »-._~!$&'()*+,;=:@/?« %HEX and alnum are allowed
// the RFC explicitly states ?/foo being a valid use case, no mention of parameter syntax!
// URI.js treats the query string as being application/x-www-form-urlencoded
// see http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type
var t = '';
Object.getOwnPropertyNames(queryHash).forEach(function(key) {
if (Array.isArray(queryHash[key])) {
var unique = {};
for (var i = 0, length = queryHash[key].length; i < length; i++) {
if (queryHash[key][i] !== undefined && unique[queryHash[key][i] + ""] === undefined) {
t += "&" + buildQueryParameter(key, queryHash[key][i]);
if (duplicates !== true) {
unique[queryHash[key][i] + ""] = true;
}
}
}
} else if (queryHash[key] !== undefined) {
t += '&' + buildQueryParameter(key, queryHash[key]);
}
});
return url + '?'+ t.substring(1);
};
/**
* creates the ability to add query params
* via a builder pattern
*
* @param key - the name of the query param,
* can also be a dictionary
*
* @param value - the value of such
*
* credits: adopted from URI.js
*/
this.addQuery = function (name, value) {
if (typeof name === "object") {
for (var key in Object.getOwnPropertyNames(name)) {
if (queryHash[key]) {
throw new Error('addQuery() accepts unique params, ' + name + ' already exists.');
}
queryHash[key] = name[key];
}
} else if (typeof name === "string") {
if (queryHash[name] === undefined) {
queryHash[name] = value;
return;
} else if (typeof queryHash[name] === "string") {
queryHash[name] = [queryHash[name]];
}
if (!Array.isArray(value)) {
value = [value];
}
queryHash[name] = queryHash[name].concat(value);
} else {
throw new TypeError("addQuery() accepts an object, string as the name parameter");
}
};
return this;
},
endsWith = function (str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
},
formatFilterQuery = function (filter) {
var filterQuery,
operand = filter.operand,
// underscore reports that NaN is a number. How worthless is that.
isNumber = typeof (+filter.operand) === 'number' && !isNaN(+filter.operand),
isBoolean = _.isBoolean(filter.operand);
if (!isNumber && !isBoolean) {
operand = "'" + filter.operand + "'";
}
filterQuery = filter.name + ' ' + filter.operator + ' ' + operand;
return filterQuery;
},
ODataCriteriaBuilder = function (url) {
// Enforce construction
if (!(this instanceof ODataCriteriaBuilder)) {
return new ODataCriteriaBuilder(url);
}
if (typeof url !== "string") {
return new Error('Url is expected to be a string.');
}
this.url = new Url(url);
};
ODataCriteriaBuilder.prototype.addTopResults = function (maxResults) {
if (_.isNumber(maxResults)) {
this.url.addQuery('$top', maxResults);
}
return this;
};
ODataCriteriaBuilder.prototype.addFilter = function (filter) {
var filterQuery = formatFilterQuery(filter);
this.url.addQuery('$filter', filterQuery);
return this;
};
ODataCriteriaBuilder.prototype.addArrayFilter = function (filter, arrayFilter, comparison) {
var filterQuery = '';
comparison = comparison || 'OR';
comparison = ' ' + comparison.trim() + ' ';
_(arrayFilter).each(function (item) {
filter.operand = item;
filterQuery += '(' + formatFilterQuery(filter) + ')' + comparison;
});
if (endsWith(filterQuery, ')' + comparison)) {
filterQuery = filterQuery.substring(0, filterQuery.lastIndexOf(')' + comparison) + 1);
}
if (filterQuery !== '') {
this.url.addQuery('$filter', filterQuery);
}
};
ODataCriteriaBuilder.prototype.addSince = function (sinceDate) {
this.url.addQuery('since', moment(sinceDate).format("MM\/DD\/YYYY"));
};
ODataCriteriaBuilder.prototype.query = function () {
/**
* builds the queryHash and returns the
* url with parameters and values included
*
*/
return this.url.buildQuery();
};
return ODataCriteriaBuilder;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment