Created
June 11, 2013 22:26
-
-
Save samdelagarza/5761308 to your computer and use it in GitHub Desktop.
Simple oData builder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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