services.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. /*jshint globalstrict:true */
  2. /*global angular:true */
  3. /*global Blob:false*/
  4. 'use strict';
  5. angular.module('kibana.services', [])
  6. .service('fields', function(dashboard, $rootScope, $http) {
  7. // Save a reference to this
  8. var self = this;
  9. this.list = ['_type'];
  10. this.mapping = {};
  11. this.add_fields = function(f) {
  12. //self.list = _.union(f,self.list);
  13. };
  14. $rootScope.$watch(function(){return dashboard.indices;},function(n) {
  15. if(!_.isUndefined(n) && n.length) {
  16. // Only get the mapping for indices we don't know it for
  17. var indices = _.difference(n,_.keys(self.mapping));
  18. // Only get the mapping if there are indices
  19. if(indices.length > 0) {
  20. self.map(indices).then(function(result) {
  21. self.mapping = _.extend(self.mapping,result);
  22. self.list = mapFields(self.mapping);
  23. });
  24. // Otherwise just use the cached mapping
  25. } else {
  26. self.list = mapFields(_.pick(self.mapping,n));
  27. }
  28. }
  29. });
  30. var mapFields = function (m) {
  31. var fields = [];
  32. _.each(m, function(types,index) {
  33. _.each(types, function(v,k) {
  34. fields = _.union(fields,_.keys(v));
  35. });
  36. });
  37. return fields;
  38. };
  39. this.map = function(indices) {
  40. var request = $http({
  41. url: config.elasticsearch + "/" + indices.join(',') + "/_mapping",
  42. method: "GET"
  43. });
  44. return request.then(function(p) {
  45. var mapping = {};
  46. _.each(p.data, function(v,k) {
  47. mapping[k] = {};
  48. _.each(v, function (v,f) {
  49. mapping[k][f] = flatten(v);
  50. });
  51. });
  52. return mapping;
  53. });
  54. };
  55. var flatten = function(obj,prefix) {
  56. var propName = (prefix) ? prefix : '',
  57. dot = (prefix) ? '.':'',
  58. ret = {};
  59. for(var attr in obj){
  60. // For now only support multi field on the top level
  61. // and if if there is a default field set.
  62. if(obj[attr]['type'] === 'multi_field') {
  63. ret[attr] = obj[attr]['fields'][attr] || obj[attr];
  64. continue;
  65. }
  66. if (attr === 'properties') {
  67. _.extend(ret,flatten(obj[attr], propName));
  68. } else if(typeof obj[attr] === 'object'){
  69. _.extend(ret,flatten(obj[attr], propName + dot + attr));
  70. } else {
  71. ret[propName] = obj;
  72. }
  73. }
  74. return ret;
  75. };
  76. })
  77. .service('kbnIndex',function($http) {
  78. // returns a promise containing an array of all indices matching the index
  79. // pattern that exist in a given range
  80. this.indices = function(from,to,pattern,interval) {
  81. var possible = [];
  82. _.each(expand_range(fake_utc(from),fake_utc(to),interval),function(d){
  83. possible.push(d.format(pattern));
  84. });
  85. return all_indices().then(function(p) {
  86. var indices = _.intersection(possible,p);
  87. indices.reverse();
  88. return indices;
  89. });
  90. };
  91. // returns a promise containing an array of all indices in an elasticsearch
  92. // cluster
  93. function all_indices() {
  94. var something = $http({
  95. url: config.elasticsearch + "/_aliases",
  96. method: "GET"
  97. }).error(function(data, status, headers, config) {
  98. // Handle error condition somehow?
  99. });
  100. return something.then(function(p) {
  101. var indices = [];
  102. _.each(p.data, function(v,k) {
  103. indices.push(k);
  104. // Also add the aliases. Could be expensive on systems with a lot of them
  105. _.each(v.aliases, function(v, k) {
  106. indices.push(k);
  107. });
  108. });
  109. return indices;
  110. });
  111. }
  112. // this is stupid, but there is otherwise no good way to ensure that when
  113. // I extract the date from an object that I get the UTC date. Stupid js.
  114. // I die a little inside every time I call this function.
  115. // Update: I just read this again. I died a little more inside.
  116. // Update2: More death.
  117. function fake_utc(date) {
  118. date = moment(date).clone().toDate();
  119. return moment(new Date(date.getTime() + date.getTimezoneOffset() * 60000));
  120. }
  121. // Create an array of date objects by a given interval
  122. function expand_range(start, end, interval) {
  123. if(_.contains(['hour','day','week','month','year'],interval)) {
  124. var range;
  125. start = moment(start).clone();
  126. range = [];
  127. while (start.isBefore(end)) {
  128. range.push(start.clone());
  129. switch (interval) {
  130. case 'hour':
  131. start.add('hours',1);
  132. break;
  133. case 'day':
  134. start.add('days',1);
  135. break;
  136. case 'week':
  137. start.add('weeks',1);
  138. break;
  139. case 'month':
  140. start.add('months',1);
  141. break;
  142. case 'year':
  143. start.add('years',1);
  144. break;
  145. }
  146. }
  147. range.push(moment(end).clone());
  148. return range;
  149. } else {
  150. return false;
  151. }
  152. }
  153. })
  154. .service('timer', function($timeout) {
  155. // This service really just tracks a list of $timeout promises to give us a
  156. // method for cancelling them all when we need to
  157. var timers = [];
  158. this.register = function(promise) {
  159. timers.push(promise);
  160. return promise;
  161. };
  162. this.cancel = function(promise) {
  163. timers = _.without(timers,promise);
  164. $timeout.cancel(promise);
  165. };
  166. this.cancel_all = function() {
  167. _.each(timers, function(t){
  168. $timeout.cancel(t);
  169. });
  170. timers = [];
  171. };
  172. })
  173. .service('querySrv', function(dashboard, ejsResource) {
  174. // Create an object to hold our service state on the dashboard
  175. dashboard.current.services.query = dashboard.current.services.query || {};
  176. _.defaults(dashboard.current.services.query,{
  177. idQueue : [],
  178. list : {},
  179. ids : [],
  180. });
  181. // For convenience
  182. var ejs = ejsResource(config.elasticsearch);
  183. var _q = dashboard.current.services.query;
  184. this.colors = [
  185. "#7EB26D","#EAB839","#6ED0E0","#EF843C","#E24D42","#1F78C1","#BA43A9","#705DA0", //1
  186. "#508642","#CCA300","#447EBC","#C15C17","#890F02","#0A437C","#6D1F62","#584477", //2
  187. "#B7DBAB","#F4D598","#70DBED","#F9BA8F","#F29191","#82B5D8","#E5A8E2","#AEA2E0", //3
  188. "#629E51","#E5AC0E","#64B0C8","#E0752D","#BF1B00","#0A50A1","#962D82","#614D93", //4
  189. "#9AC48A","#F2C96D","#65C5DB","#F9934E","#EA6460","#5195CE","#D683CE","#806EB7", //5
  190. "#3F6833","#967302","#2F575E","#99440A","#58140C","#052B51","#511749","#3F2B5B", //6
  191. "#E0F9D7","#FCEACA","#CFFAFF","#F9E2D2","#FCE2DE","#BADFF4","#F9D9F9","#DEDAF7" //7
  192. ];
  193. // Save a reference to this
  194. var self = this;
  195. this.init = function() {
  196. _q = dashboard.current.services.query;
  197. self.list = dashboard.current.services.query.list;
  198. self.ids = dashboard.current.services.query.ids;
  199. if (self.ids.length === 0) {
  200. self.set({});
  201. }
  202. };
  203. // This is used both for adding queries and modifying them. If an id is passed, the query at that id is updated
  204. this.set = function(query,id) {
  205. if(!_.isUndefined(id)) {
  206. if(!_.isUndefined(self.list[id])) {
  207. _.extend(self.list[id],query);
  208. return id;
  209. } else {
  210. return false;
  211. }
  212. } else {
  213. var _id = nextId();
  214. var _query = {
  215. query: '*',
  216. alias: '',
  217. color: colorAt(_id),
  218. pin: false,
  219. id: _id,
  220. type: 'lucene'
  221. };
  222. _.defaults(query,_query);
  223. self.list[_id] = query;
  224. self.ids.push(_id);
  225. return _id;
  226. }
  227. };
  228. this.remove = function(id) {
  229. if(!_.isUndefined(self.list[id])) {
  230. delete self.list[id];
  231. // This must happen on the full path also since _.without returns a copy
  232. self.ids = dashboard.current.services.query.ids = _.without(self.ids,id);
  233. _q.idQueue.unshift(id);
  234. _q.idQueue.sort(function(v,k){
  235. return v-k;
  236. });
  237. return true;
  238. } else {
  239. return false;
  240. }
  241. };
  242. this.getEjsObj = function(id) {
  243. return self.toEjsObj(self.list[id]);
  244. };
  245. this.toEjsObj = function (q) {
  246. switch(q.type)
  247. {
  248. case 'lucene':
  249. return ejs.QueryStringQuery(q.query || '*');
  250. default:
  251. return _.isUndefined(q.query) ? false : ejs.QueryStringQuery(q.query || '*');
  252. }
  253. };
  254. this.findQuery = function(queryString) {
  255. return _.findWhere(self.list,{query:queryString});
  256. };
  257. this.idsByMode = function(config) {
  258. switch(config.mode)
  259. {
  260. case 'all':
  261. return self.ids;
  262. case 'pinned':
  263. return _.pluck(_.where(self.list,{pin:true}),'id');
  264. case 'unpinned':
  265. return _.difference(self.ids,_.pluck(_.where(self.list,{pin:true}),'id'));
  266. case 'selected':
  267. return _.intersection(self.ids,config.ids);
  268. default:
  269. return self.ids;
  270. }
  271. };
  272. var nextId = function() {
  273. if(_q.idQueue.length > 0) {
  274. return _q.idQueue.shift();
  275. } else {
  276. return self.ids.length;
  277. }
  278. };
  279. var colorAt = function(id) {
  280. return self.colors[id % self.colors.length];
  281. };
  282. self.init();
  283. })
  284. .service('filterSrv', function(dashboard, ejsResource) {
  285. // Create an object to hold our service state on the dashboard
  286. dashboard.current.services.filter = dashboard.current.services.filter || {};
  287. _.defaults(dashboard.current.services.filter,{
  288. idQueue : [],
  289. list : {},
  290. ids : []
  291. });
  292. // For convenience
  293. var ejs = ejsResource(config.elasticsearch);
  294. var _f = dashboard.current.services.filter;
  295. // Save a reference to this
  296. var self = this;
  297. // Call this whenever we need to reload the important stuff
  298. this.init = function() {
  299. // Accessors
  300. self.list = dashboard.current.services.filter.list;
  301. self.ids = dashboard.current.services.filter.ids;
  302. _f = dashboard.current.services.filter;
  303. _.each(self.getByType('time',true),function(time) {
  304. self.list[time.id].from = new Date(time.from);
  305. self.list[time.id].to = new Date(time.to);
  306. });
  307. };
  308. // This is used both for adding filters and modifying them.
  309. // If an id is passed, the filter at that id is updated
  310. this.set = function(filter,id) {
  311. _.defaults(filter,{mandate:'must'});
  312. filter.active = true;
  313. if(!_.isUndefined(id)) {
  314. if(!_.isUndefined(self.list[id])) {
  315. _.extend(self.list[id],filter);
  316. return id;
  317. } else {
  318. return false;
  319. }
  320. } else {
  321. if(_.isUndefined(filter.type)) {
  322. return false;
  323. } else {
  324. var _id = nextId();
  325. var _filter = {
  326. alias: '',
  327. id: _id
  328. };
  329. _.defaults(filter,_filter);
  330. self.list[_id] = filter;
  331. self.ids.push(_id);
  332. return _id;
  333. }
  334. }
  335. };
  336. this.getBoolFilter = function(ids) {
  337. // A default match all filter, just in case there are no other filters
  338. var bool = ejs.BoolFilter().must(ejs.MatchAllFilter());
  339. var either_bool = ejs.BoolFilter().must(ejs.MatchAllFilter());
  340. _.each(ids,function(id) {
  341. if(self.list[id].active) {
  342. switch(self.list[id].mandate)
  343. {
  344. case 'mustNot':
  345. bool = bool.mustNot(self.getEjsObj(id));
  346. break;
  347. case 'either':
  348. either_bool = either_bool.should(self.getEjsObj(id));
  349. break;
  350. default:
  351. bool = bool.must(self.getEjsObj(id));
  352. }
  353. }
  354. });
  355. return bool.must(either_bool);
  356. };
  357. this.getEjsObj = function(id) {
  358. return self.toEjsObj(self.list[id]);
  359. };
  360. this.toEjsObj = function (filter) {
  361. if(!filter.active) {
  362. return false;
  363. }
  364. switch(filter.type)
  365. {
  366. case 'time':
  367. return ejs.RangeFilter(filter.field)
  368. .from(filter.from.valueOf())
  369. .to(filter.to.valueOf());
  370. case 'range':
  371. return ejs.RangeFilter(filter.field)
  372. .from(filter.from)
  373. .to(filter.to);
  374. case 'querystring':
  375. return ejs.QueryFilter(ejs.QueryStringQuery(filter.query)).cache(true);
  376. case 'field':
  377. return ejs.QueryFilter(ejs.FieldQuery(filter.field,filter.query)).cache(true);
  378. case 'terms':
  379. return ejs.TermsFilter(filter.field,filter.value);
  380. case 'exists':
  381. return ejs.ExistsFilter(filter.field);
  382. case 'missing':
  383. return ejs.MissingFilter(filter.field);
  384. default:
  385. return false;
  386. }
  387. };
  388. this.getByType = function(type,inactive) {
  389. return _.pick(self.list,self.idsByType(type,inactive));
  390. };
  391. this.removeByType = function(type) {
  392. var ids = self.idsByType(type);
  393. _.each(ids,function(id) {
  394. self.remove(id);
  395. });
  396. return ids;
  397. };
  398. this.idsByType = function(type,inactive) {
  399. var _require = inactive ? {type:type} : {type:type,active:true};
  400. return _.pluck(_.where(self.list,_require),'id');
  401. };
  402. // TOFIX: Error handling when there is more than one field
  403. this.timeField = function() {
  404. return _.pluck(self.getByType('time'),'field');
  405. };
  406. // This special function looks for all time filters, and returns a time range according to the mode
  407. // No idea when max would actually be used
  408. this.timeRange = function(mode) {
  409. var _t = _.where(self.list,{type:'time',active:true});
  410. if(_t.length === 0) {
  411. return false;
  412. }
  413. switch(mode) {
  414. case "min":
  415. return {
  416. from: new Date(_.max(_.pluck(_t,'from'))),
  417. to: new Date(_.min(_.pluck(_t,'to')))
  418. };
  419. case "max":
  420. return {
  421. from: new Date(_.min(_.pluck(_t,'from'))),
  422. to: new Date(_.max(_.pluck(_t,'to')))
  423. };
  424. default:
  425. return false;
  426. }
  427. };
  428. this.remove = function(id) {
  429. if(!_.isUndefined(self.list[id])) {
  430. delete self.list[id];
  431. // This must happen on the full path also since _.without returns a copy
  432. self.ids = dashboard.current.services.filter.ids = _.without(self.ids,id);
  433. _f.idQueue.unshift(id);
  434. _f.idQueue.sort(function(v,k){return v-k;});
  435. return true;
  436. } else {
  437. return false;
  438. }
  439. };
  440. var nextId = function() {
  441. if(_f.idQueue.length > 0) {
  442. return _f.idQueue.shift();
  443. } else {
  444. return self.ids.length;
  445. }
  446. };
  447. // Now init
  448. self.init();
  449. })
  450. .service('dashboard', function($routeParams, $http, $rootScope, $injector, ejsResource, timer, kbnIndex) {
  451. // A hash of defaults to use when loading a dashboard
  452. var _dash = {
  453. title: "",
  454. style: "dark",
  455. editable: true,
  456. rows: [],
  457. services: {},
  458. index: {
  459. interval: 'none',
  460. pattern: '_all',
  461. default: 'INDEX_MISSING'
  462. },
  463. };
  464. // An elasticJS client to use
  465. var ejs = ejsResource(config.elasticsearch);
  466. var gist_pattern = /(^\d{5,}$)|(^[a-z0-9]{10,}$)|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;
  467. // Store a reference to this
  468. var self = this;
  469. var filterSrv,querySrv;
  470. this.current = _.clone(_dash);
  471. this.last = {};
  472. $rootScope.$on('$routeChangeSuccess',function(){
  473. // Clear the current dashboard to prevent reloading
  474. self.current = {};
  475. self.indices = [];
  476. route();
  477. });
  478. var route = function() {
  479. // Is there a dashboard type and id in the URL?
  480. if(!(_.isUndefined($routeParams.type)) && !(_.isUndefined($routeParams.id))) {
  481. var _type = $routeParams.type;
  482. var _id = $routeParams.id;
  483. switch(_type) {
  484. case ('elasticsearch'):
  485. self.elasticsearch_load('dashboard',_id);
  486. break;
  487. case ('temp'):
  488. self.elasticsearch_load('temp',_id);
  489. break;
  490. case ('file'):
  491. self.file_load(_id);
  492. break;
  493. default:
  494. self.file_load('default.json');
  495. }
  496. // No dashboard in the URL
  497. } else {
  498. // Check if browser supports localstorage, and if there's a dashboard
  499. if (window.Modernizr.localstorage &&
  500. !(_.isUndefined(window.localStorage['dashboard'])) &&
  501. window.localStorage['dashboard'] !== ''
  502. ) {
  503. var dashboard = JSON.parse(window.localStorage['dashboard']);
  504. self.dash_load(dashboard);
  505. // No? Ok, grab default.json, its all we have now
  506. } else {
  507. self.file_load('default.json');
  508. }
  509. }
  510. };
  511. // Since the dashboard is responsible for index computation, we can compute and assign the indices
  512. // here before telling the panels to refresh
  513. this.refresh = function() {
  514. if(self.current.index.interval !== 'none') {
  515. if(filterSrv.idsByType('time').length > 0) {
  516. var _range = filterSrv.timeRange('min');
  517. kbnIndex.indices(_range.from,_range.to,
  518. self.current.index.pattern,self.current.index.interval
  519. ).then(function (p) {
  520. if(p.length > 0) {
  521. self.indices = p;
  522. } else {
  523. self.indices = [self.current.index.default];
  524. }
  525. $rootScope.$broadcast('refresh');
  526. });
  527. } else {
  528. // This is not optimal, we should be getting the entire index list here, or at least every
  529. // index that possibly matches the pattern
  530. self.indices = [self.current.index.default];
  531. $rootScope.$broadcast('refresh');
  532. }
  533. } else {
  534. self.indices = [self.current.index.default];
  535. $rootScope.$broadcast('refresh');
  536. }
  537. };
  538. this.dash_load = function(dashboard) {
  539. // Cancel all timers
  540. timer.cancel_all();
  541. // Make sure the dashboard being loaded has everything required
  542. _.defaults(dashboard,_dash);
  543. // If not using time based indices, use the default index
  544. if(dashboard.index.interval === 'none') {
  545. self.indices = [dashboard.index.default];
  546. }
  547. self.current = _.clone(dashboard);
  548. // Ok, now that we've setup the current dashboard, we can inject our services
  549. querySrv = $injector.get('querySrv');
  550. filterSrv = $injector.get('filterSrv');
  551. // Make sure these re-init
  552. querySrv.init();
  553. filterSrv.init();
  554. // If there's an index interval set and no existing time filter, send a refresh to set one
  555. if(dashboard.index.interval !== 'none' && filterSrv.idsByType('time').length === 0) {
  556. self.refresh();
  557. }
  558. return true;
  559. };
  560. this.gist_id = function(string) {
  561. if(self.is_gist(string)) {
  562. return string.match(gist_pattern)[0].replace(/.*\//, '');
  563. }
  564. };
  565. this.is_gist = function(string) {
  566. if(!_.isUndefined(string) && string !== '' && !_.isNull(string.match(gist_pattern))) {
  567. return string.match(gist_pattern).length > 0 ? true : false;
  568. } else {
  569. return false;
  570. }
  571. };
  572. this.to_file = function() {
  573. var blob = new Blob([angular.toJson(self.current,true)], {type: "application/json;charset=utf-8"});
  574. // from filesaver.js
  575. window.saveAs(blob, self.current.title+"-"+new Date().getTime());
  576. return true;
  577. };
  578. this.set_default = function(dashboard) {
  579. if (window.Modernizr.localstorage) {
  580. window.localStorage['dashboard'] = angular.toJson(dashboard || self.current);
  581. return true;
  582. } else {
  583. return false;
  584. }
  585. };
  586. this.purge_default = function() {
  587. if (window.Modernizr.localstorage) {
  588. window.localStorage['dashboard'] = '';
  589. return true;
  590. } else {
  591. return false;
  592. }
  593. };
  594. // TOFIX: Pretty sure this breaks when you're on a saved dashboard already
  595. this.share_link = function(title,type,id) {
  596. return {
  597. location : window.location.href.replace(window.location.hash,""),
  598. type : type,
  599. id : id,
  600. link : window.location.href.replace(window.location.hash,"")+"#dashboard/"+type+"/"+id,
  601. title : title
  602. };
  603. };
  604. this.file_load = function(file) {
  605. return $http({
  606. url: "dashboards/"+file,
  607. method: "GET",
  608. }).then(function(result) {
  609. var _dashboard = result.data;
  610. _.defaults(_dashboard,_dash);
  611. self.dash_load(_dashboard);
  612. return true;
  613. },function(result) {
  614. return false;
  615. });
  616. };
  617. this.elasticsearch_load = function(type,id) {
  618. var request = ejs.Request().indices(config.kibana_index).types(type);
  619. var results = request.query(
  620. ejs.IdsQuery(id)
  621. ).doSearch();
  622. return results.then(function(results) {
  623. if(_.isUndefined(results)) {
  624. return false;
  625. } else {
  626. self.dash_load(angular.fromJson(results.hits.hits[0]['_source']['dashboard']));
  627. return true;
  628. }
  629. });
  630. };
  631. this.elasticsearch_save = function(type,title,ttl) {
  632. // Clone object so we can modify it without influencing the existing obejct
  633. var save = _.clone(self.current);
  634. var id;
  635. // Change title on object clone
  636. if (type === 'dashboard') {
  637. id = save.title = _.isUndefined(title) ? self.current.title : title;
  638. }
  639. // Create request with id as title. Rethink this.
  640. var request = ejs.Document(config.kibana_index,type,id).source({
  641. user: 'guest',
  642. group: 'guest',
  643. title: save.title,
  644. dashboard: angular.toJson(save)
  645. });
  646. request = type === 'temp' && ttl ? request.ttl(ttl) : request;
  647. // TOFIX: Implement error handling here
  648. return request.doIndex(
  649. // Success
  650. function(result) {
  651. return result;
  652. },
  653. // Failure
  654. function(result) {
  655. return false;
  656. }
  657. );
  658. };
  659. this.elasticsearch_delete = function(id) {
  660. return ejs.Document(config.kibana_index,'dashboard',id).doDelete(
  661. // Success
  662. function(result) {
  663. return result;
  664. },
  665. // Failure
  666. function(result) {
  667. return false;
  668. }
  669. );
  670. };
  671. this.elasticsearch_list = function(query,count) {
  672. var request = ejs.Request().indices(config.kibana_index).types('dashboard');
  673. return request.query(
  674. ejs.QueryStringQuery(query || '*')
  675. ).size(count).doSearch(
  676. // Success
  677. function(result) {
  678. return result;
  679. },
  680. // Failure
  681. function(result) {
  682. return false;
  683. }
  684. );
  685. };
  686. // TOFIX: Gist functionality
  687. this.save_gist = function(title,dashboard) {
  688. var save = _.clone(dashboard || self.current);
  689. save.title = title || self.current.title;
  690. return $http({
  691. url: "https://api.github.com/gists",
  692. method: "POST",
  693. data: {
  694. "description": save.title,
  695. "public": false,
  696. "files": {
  697. "kibana-dashboard.json": {
  698. "content": angular.toJson(save,true)
  699. }
  700. }
  701. }
  702. }).then(function(data, status, headers, config) {
  703. return data.data.html_url;
  704. }, function(data, status, headers, config) {
  705. return false;
  706. });
  707. };
  708. this.gist_list = function(id) {
  709. return $http.jsonp("https://api.github.com/gists/"+id+"?callback=JSON_CALLBACK"
  710. ).then(function(response) {
  711. var files = [];
  712. _.each(response.data.data.files,function(v,k) {
  713. try {
  714. var file = JSON.parse(v.content);
  715. files.push(file);
  716. } catch(e) {
  717. // Nothing?
  718. }
  719. });
  720. return files;
  721. }, function(data, status, headers, config) {
  722. return false;
  723. });
  724. };
  725. });