PageRenderTime 58ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/rel/overlay/share/www/script/couch.js

http://github.com/cloudant/bigcouch
JavaScript | 473 lines | 392 code | 49 blank | 32 comment | 90 complexity | 82e55bc07d6c3b4491c49f855aff2461 MD5 | raw file
Possible License(s): Apache-2.0
  1. // Licensed under the Apache License, Version 2.0 (the "License"); you may not
  2. // use this file except in compliance with the License. You may obtain a copy of
  3. // the License at
  4. //
  5. // http://www.apache.org/licenses/LICENSE-2.0
  6. //
  7. // Unless required by applicable law or agreed to in writing, software
  8. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  9. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  10. // License for the specific language governing permissions and limitations under
  11. // the License.
  12. // A simple class to represent a database. Uses XMLHttpRequest to interface with
  13. // the CouchDB server.
  14. function CouchDB(name, httpHeaders) {
  15. this.name = name;
  16. this.uri = "/" + encodeURIComponent(name) + "/";
  17. // The XMLHttpRequest object from the most recent request. Callers can
  18. // use this to check result http status and headers.
  19. this.last_req = null;
  20. this.request = function(method, uri, requestOptions) {
  21. requestOptions = requestOptions || {};
  22. requestOptions.headers = combine(requestOptions.headers, httpHeaders);
  23. return CouchDB.request(method, uri, requestOptions);
  24. };
  25. // Creates the database on the server
  26. this.createDb = function() {
  27. this.last_req = this.request("PUT", this.uri);
  28. CouchDB.maybeThrowError(this.last_req);
  29. return JSON.parse(this.last_req.responseText);
  30. };
  31. // Deletes the database on the server
  32. this.deleteDb = function() {
  33. this.last_req = this.request("DELETE", this.uri);
  34. if (this.last_req.status == 404) {
  35. return false;
  36. }
  37. CouchDB.maybeThrowError(this.last_req);
  38. return JSON.parse(this.last_req.responseText);
  39. };
  40. // Save a document to the database
  41. this.save = function(doc, options) {
  42. if (doc._id == undefined) {
  43. doc._id = CouchDB.newUuids(1)[0];
  44. }
  45. this.last_req = this.request("PUT", this.uri +
  46. encodeURIComponent(doc._id) + encodeOptions(options),
  47. {body: JSON.stringify(doc)});
  48. CouchDB.maybeThrowError(this.last_req);
  49. var result = JSON.parse(this.last_req.responseText);
  50. doc._rev = result.rev;
  51. return result;
  52. };
  53. // Open a document from the database
  54. this.open = function(docId, options) {
  55. this.last_req = this.request("GET", this.uri + encodeURIComponent(docId)
  56. + encodeOptions(options));
  57. if (this.last_req.status == 404) {
  58. return null;
  59. }
  60. CouchDB.maybeThrowError(this.last_req);
  61. return JSON.parse(this.last_req.responseText);
  62. };
  63. // Deletes a document from the database
  64. this.deleteDoc = function(doc) {
  65. this.last_req = this.request("DELETE", this.uri + encodeURIComponent(doc._id)
  66. + "?rev=" + doc._rev);
  67. CouchDB.maybeThrowError(this.last_req);
  68. var result = JSON.parse(this.last_req.responseText);
  69. doc._rev = result.rev; //record rev in input document
  70. doc._deleted = true;
  71. return result;
  72. };
  73. // Deletes an attachment from a document
  74. this.deleteDocAttachment = function(doc, attachment_name) {
  75. this.last_req = this.request("DELETE", this.uri + encodeURIComponent(doc._id)
  76. + "/" + attachment_name + "?rev=" + doc._rev);
  77. CouchDB.maybeThrowError(this.last_req);
  78. var result = JSON.parse(this.last_req.responseText);
  79. doc._rev = result.rev; //record rev in input document
  80. return result;
  81. };
  82. this.bulkSave = function(docs, options) {
  83. // first prepoulate the UUIDs for new documents
  84. var newCount = 0;
  85. for (var i=0; i<docs.length; i++) {
  86. if (docs[i]._id == undefined) {
  87. newCount++;
  88. }
  89. }
  90. var newUuids = CouchDB.newUuids(docs.length);
  91. var newCount = 0;
  92. for (var i=0; i<docs.length; i++) {
  93. if (docs[i]._id == undefined) {
  94. docs[i]._id = newUuids.pop();
  95. }
  96. }
  97. var json = {"docs": docs};
  98. // put any options in the json
  99. for (var option in options) {
  100. json[option] = options[option];
  101. }
  102. this.last_req = this.request("POST", this.uri + "_bulk_docs", {
  103. body: JSON.stringify(json)
  104. });
  105. if (this.last_req.status == 417) {
  106. return {errors: JSON.parse(this.last_req.responseText)};
  107. }
  108. else {
  109. CouchDB.maybeThrowError(this.last_req);
  110. var results = JSON.parse(this.last_req.responseText);
  111. for (var i = 0; i < docs.length; i++) {
  112. if(results[i] && results[i].rev) {
  113. docs[i]._rev = results[i].rev;
  114. }
  115. }
  116. return results;
  117. }
  118. };
  119. this.ensureFullCommit = function() {
  120. this.last_req = this.request("POST", this.uri + "_ensure_full_commit");
  121. CouchDB.maybeThrowError(this.last_req);
  122. return JSON.parse(this.last_req.responseText);
  123. };
  124. // Applies the map function to the contents of database and returns the results.
  125. this.query = function(mapFun, reduceFun, options, keys, language) {
  126. var body = {language: language || "javascript"};
  127. if(keys) {
  128. body.keys = keys ;
  129. }
  130. if (typeof(mapFun) != "string") {
  131. mapFun = mapFun.toSource ? mapFun.toSource() : "(" + mapFun.toString() + ")";
  132. }
  133. body.map = mapFun;
  134. if (reduceFun != null) {
  135. if (typeof(reduceFun) != "string") {
  136. reduceFun = reduceFun.toSource ?
  137. reduceFun.toSource() : "(" + reduceFun.toString() + ")";
  138. }
  139. body.reduce = reduceFun;
  140. }
  141. if (options && options.options != undefined) {
  142. body.options = options.options;
  143. delete options.options;
  144. }
  145. this.last_req = this.request("POST", this.uri + "_temp_view"
  146. + encodeOptions(options), {
  147. headers: {"Content-Type": "application/json"},
  148. body: JSON.stringify(body)
  149. });
  150. CouchDB.maybeThrowError(this.last_req);
  151. return JSON.parse(this.last_req.responseText);
  152. };
  153. this.view = function(viewname, options, keys) {
  154. var viewParts = viewname.split('/');
  155. var viewPath = this.uri + "_design/" + viewParts[0] + "/_view/"
  156. + viewParts[1] + encodeOptions(options);
  157. if(!keys) {
  158. this.last_req = this.request("GET", viewPath);
  159. } else {
  160. this.last_req = this.request("POST", viewPath, {
  161. headers: {"Content-Type": "application/json"},
  162. body: JSON.stringify({keys:keys})
  163. });
  164. }
  165. if (this.last_req.status == 404) {
  166. return null;
  167. }
  168. CouchDB.maybeThrowError(this.last_req);
  169. return JSON.parse(this.last_req.responseText);
  170. };
  171. // gets information about the database
  172. this.info = function() {
  173. this.last_req = this.request("GET", this.uri);
  174. CouchDB.maybeThrowError(this.last_req);
  175. return JSON.parse(this.last_req.responseText);
  176. };
  177. // gets information about a design doc
  178. this.designInfo = function(docid) {
  179. this.last_req = this.request("GET", this.uri + docid + "/_info");
  180. CouchDB.maybeThrowError(this.last_req);
  181. return JSON.parse(this.last_req.responseText);
  182. };
  183. this.allDocs = function(options,keys) {
  184. if(!keys) {
  185. this.last_req = this.request("GET", this.uri + "_all_docs"
  186. + encodeOptions(options));
  187. } else {
  188. this.last_req = this.request("POST", this.uri + "_all_docs"
  189. + encodeOptions(options), {
  190. headers: {"Content-Type": "application/json"},
  191. body: JSON.stringify({keys:keys})
  192. });
  193. }
  194. CouchDB.maybeThrowError(this.last_req);
  195. return JSON.parse(this.last_req.responseText);
  196. };
  197. this.designDocs = function() {
  198. return this.allDocs({startkey:"_design", endkey:"_design0"});
  199. };
  200. this.changes = function(options) {
  201. this.last_req = this.request("GET", this.uri + "_changes"
  202. + encodeOptions(options));
  203. CouchDB.maybeThrowError(this.last_req);
  204. return JSON.parse(this.last_req.responseText);
  205. };
  206. this.compact = function() {
  207. this.last_req = this.request("POST", this.uri + "_compact");
  208. CouchDB.maybeThrowError(this.last_req);
  209. return JSON.parse(this.last_req.responseText);
  210. };
  211. this.viewCleanup = function() {
  212. this.last_req = this.request("POST", this.uri + "_view_cleanup");
  213. CouchDB.maybeThrowError(this.last_req);
  214. return JSON.parse(this.last_req.responseText);
  215. };
  216. this.setDbProperty = function(propId, propValue) {
  217. this.last_req = this.request("PUT", this.uri + propId,{
  218. body:JSON.stringify(propValue)
  219. });
  220. CouchDB.maybeThrowError(this.last_req);
  221. return JSON.parse(this.last_req.responseText);
  222. };
  223. this.getDbProperty = function(propId) {
  224. this.last_req = this.request("GET", this.uri + propId);
  225. CouchDB.maybeThrowError(this.last_req);
  226. return JSON.parse(this.last_req.responseText);
  227. };
  228. this.setSecObj = function(secObj) {
  229. this.last_req = this.request("PUT", this.uri + "_security",{
  230. body:JSON.stringify(secObj)
  231. });
  232. CouchDB.maybeThrowError(this.last_req);
  233. return JSON.parse(this.last_req.responseText);
  234. };
  235. this.getSecObj = function() {
  236. this.last_req = this.request("GET", this.uri + "_security");
  237. CouchDB.maybeThrowError(this.last_req);
  238. return JSON.parse(this.last_req.responseText);
  239. };
  240. // Convert a options object to an url query string.
  241. // ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"'
  242. function encodeOptions(options) {
  243. var buf = [];
  244. if (typeof(options) == "object" && options !== null) {
  245. for (var name in options) {
  246. if (!options.hasOwnProperty(name)) { continue; };
  247. var value = options[name];
  248. if (name == "key" || name == "keys" || name == "startkey" || name == "endkey") {
  249. value = toJSON(value);
  250. }
  251. buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));
  252. }
  253. }
  254. if (!buf.length) {
  255. return "";
  256. }
  257. return "?" + buf.join("&");
  258. }
  259. function toJSON(obj) {
  260. return obj !== null ? JSON.stringify(obj) : null;
  261. }
  262. function combine(object1, object2) {
  263. if (!object2) {
  264. return object1;
  265. }
  266. if (!object1) {
  267. return object2;
  268. }
  269. for (var name in object2) {
  270. object1[name] = object2[name];
  271. }
  272. return object1;
  273. }
  274. }
  275. // this is the XMLHttpRequest object from last request made by the following
  276. // CouchDB.* functions (except for calls to request itself).
  277. // Use this from callers to check HTTP status or header values of requests.
  278. CouchDB.last_req = null;
  279. CouchDB.urlPrefix = '';
  280. CouchDB.login = function(name, password) {
  281. CouchDB.last_req = CouchDB.request("POST", "/_session", {
  282. headers: {"Content-Type": "application/x-www-form-urlencoded",
  283. "X-CouchDB-WWW-Authenticate": "Cookie"},
  284. body: "name=" + encodeURIComponent(name) + "&password="
  285. + encodeURIComponent(password)
  286. });
  287. return JSON.parse(CouchDB.last_req.responseText);
  288. };
  289. CouchDB.logout = function() {
  290. CouchDB.last_req = CouchDB.request("DELETE", "/_session", {
  291. headers: {"Content-Type": "application/x-www-form-urlencoded",
  292. "X-CouchDB-WWW-Authenticate": "Cookie"}
  293. });
  294. return JSON.parse(CouchDB.last_req.responseText);
  295. };
  296. CouchDB.session = function(options) {
  297. options = options || {};
  298. CouchDB.last_req = CouchDB.request("GET", "/_session", options);
  299. CouchDB.maybeThrowError(CouchDB.last_req);
  300. return JSON.parse(CouchDB.last_req.responseText);
  301. };
  302. CouchDB.user_prefix = "org.couchdb.user:";
  303. CouchDB.prepareUserDoc = function(user_doc, new_password) {
  304. user_doc._id = user_doc._id || CouchDB.user_prefix + user_doc.name;
  305. if (new_password) {
  306. // handle the password crypto
  307. user_doc.salt = CouchDB.newUuids(1)[0];
  308. user_doc.password_sha = hex_sha1(new_password + user_doc.salt);
  309. }
  310. user_doc.type = "user";
  311. if (!user_doc.roles) {
  312. user_doc.roles = [];
  313. }
  314. return user_doc;
  315. };
  316. CouchDB.allDbs = function() {
  317. CouchDB.last_req = CouchDB.request("GET", "/_all_dbs");
  318. CouchDB.maybeThrowError(CouchDB.last_req);
  319. return JSON.parse(CouchDB.last_req.responseText);
  320. };
  321. CouchDB.allDesignDocs = function() {
  322. var ddocs = {}, dbs = CouchDB.allDbs();
  323. for (var i=0; i < dbs.length; i++) {
  324. var db = new CouchDB(dbs[i]);
  325. ddocs[dbs[i]] = db.designDocs();
  326. };
  327. return ddocs;
  328. };
  329. CouchDB.getVersion = function() {
  330. CouchDB.last_req = CouchDB.request("GET", "/");
  331. CouchDB.maybeThrowError(CouchDB.last_req);
  332. return JSON.parse(CouchDB.last_req.responseText).version;
  333. };
  334. CouchDB.replicate = function(source, target, rep_options) {
  335. rep_options = rep_options || {};
  336. var headers = rep_options.headers || {};
  337. var body = rep_options.body || {};
  338. body.source = source;
  339. body.target = target;
  340. CouchDB.last_req = CouchDB.request("POST", "/_replicate", {
  341. headers: headers,
  342. body: JSON.stringify(body)
  343. });
  344. CouchDB.maybeThrowError(CouchDB.last_req);
  345. return JSON.parse(CouchDB.last_req.responseText);
  346. };
  347. CouchDB.newXhr = function() {
  348. if (typeof(XMLHttpRequest) != "undefined") {
  349. return new XMLHttpRequest();
  350. } else if (typeof(ActiveXObject) != "undefined") {
  351. return new ActiveXObject("Microsoft.XMLHTTP");
  352. } else {
  353. throw new Error("No XMLHTTPRequest support detected");
  354. }
  355. };
  356. CouchDB.request = function(method, uri, options) {
  357. options = typeof(options) == 'object' ? options : {};
  358. options.headers = typeof(options.headers) == 'object' ? options.headers : {};
  359. options.headers["Content-Type"] = options.headers["Content-Type"] || options.headers["content-type"] || "application/json";
  360. options.headers["Accept"] = options.headers["Accept"] || options.headers["accept"] || "application/json";
  361. var req = CouchDB.newXhr();
  362. if(uri.substr(0, CouchDB.protocol.length) != CouchDB.protocol) {
  363. uri = CouchDB.urlPrefix + uri;
  364. }
  365. req.open(method, uri, false);
  366. if (options.headers) {
  367. var headers = options.headers;
  368. for (var headerName in headers) {
  369. if (!headers.hasOwnProperty(headerName)) { continue; }
  370. req.setRequestHeader(headerName, headers[headerName]);
  371. }
  372. }
  373. req.send(options.body || "");
  374. return req;
  375. };
  376. CouchDB.requestStats = function(module, key, test) {
  377. var query_arg = "";
  378. if(test !== null) {
  379. query_arg = "?flush=true";
  380. }
  381. var url = "/_stats/" + module + "/" + key + query_arg;
  382. var stat = CouchDB.request("GET", url).responseText;
  383. return JSON.parse(stat)[module][key];
  384. };
  385. CouchDB.uuids_cache = [];
  386. CouchDB.newUuids = function(n, buf) {
  387. buf = buf || 100;
  388. if (CouchDB.uuids_cache.length >= n) {
  389. var uuids = CouchDB.uuids_cache.slice(CouchDB.uuids_cache.length - n);
  390. if(CouchDB.uuids_cache.length - n == 0) {
  391. CouchDB.uuids_cache = [];
  392. } else {
  393. CouchDB.uuids_cache =
  394. CouchDB.uuids_cache.slice(0, CouchDB.uuids_cache.length - n);
  395. }
  396. return uuids;
  397. } else {
  398. CouchDB.last_req = CouchDB.request("GET", "/_uuids?count=" + (buf + n));
  399. CouchDB.maybeThrowError(CouchDB.last_req);
  400. var result = JSON.parse(CouchDB.last_req.responseText);
  401. CouchDB.uuids_cache =
  402. CouchDB.uuids_cache.concat(result.uuids.slice(0, buf));
  403. return result.uuids.slice(buf);
  404. }
  405. };
  406. CouchDB.maybeThrowError = function(req) {
  407. if (req.status >= 400) {
  408. try {
  409. var result = JSON.parse(req.responseText);
  410. } catch (ParseError) {
  411. var result = {error:"unknown", reason:req.responseText};
  412. }
  413. throw result;
  414. }
  415. };
  416. CouchDB.params = function(options) {
  417. options = options || {};
  418. var returnArray = [];
  419. for(var key in options) {
  420. var value = options[key];
  421. returnArray.push(key + "=" + value);
  422. }
  423. return returnArray.join("&");
  424. };