pig.ko.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. // Licensed to Cloudera, Inc. under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. Cloudera, Inc. licenses this file
  5. // to you under the Apache License, Version 2.0 (the
  6. // "License"); you may not use this file except in compliance
  7. // with the License. You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. var Resource = function (resource) {
  17. var self = this;
  18. self.type = ko.observable(resource.type);
  19. self.value = ko.observable(resource.value);
  20. };
  21. var HadoopProperty = function (property) {
  22. var self = this;
  23. self.name = ko.observable(property.name);
  24. self.value = ko.observable(property.value);
  25. };
  26. var PigParameter = HadoopProperty;
  27. var PigScript = function (pigScript) {
  28. var self = this;
  29. self.id = ko.observable(pigScript.id);
  30. self.docId = ko.observable(pigScript.docId);
  31. self.isDesign = ko.observable(pigScript.isDesign);
  32. self.name = ko.observable(pigScript.name);
  33. self.can_write = ko.observable(pigScript.can_write);
  34. self.script = ko.observable(pigScript.script);
  35. self.scriptSumup = ko.observable(pigScript.script.replace(/\W+/g, ' ').substring(0, 100));
  36. self.isRunning = ko.observable(false);
  37. self.selected = ko.observable(false);
  38. self.watchUrl = ko.observable("");
  39. self.actions = ko.observableArray([]);
  40. self.handleSelect = function (row, e) {
  41. this.selected(!this.selected());
  42. };
  43. self.hovered = ko.observable(false);
  44. self.toggleHover = function (row, e) {
  45. this.hovered(!this.hovered());
  46. };
  47. self.parameters = ko.observableArray([]);
  48. ko.utils.arrayForEach(pigScript.parameters, function (parameter) {
  49. self.parameters.push(new PigParameter({name: parameter.name, value: parameter.value}));
  50. });
  51. self.addParameter = function () {
  52. self.parameters.push(new PigParameter({name: '', value: ''}));
  53. self.updateParentModel();
  54. };
  55. self.removeParameter = function () {
  56. self.parameters.remove(this);
  57. self.updateParentModel();
  58. };
  59. self.getParameters = function () {
  60. var params = {};
  61. var variables = this.script().match(/([^\\]|^)\$[^\d'"](\w*)/g);
  62. var macro_defines = this.script().match(/define [^ ]+ *\(([^\)]*)\)/gi); // no multiline
  63. var macro_returns = this.script().match(/returns +([^\{]*)/gi); // no multiline
  64. if (variables) {
  65. $.each(variables, function(index, param) {
  66. var p = param.substring(param.indexOf('$') + 1);
  67. params[p] = '';
  68. });
  69. }
  70. if (macro_defines) {
  71. $.each(macro_defines, function(index, params_line) {
  72. var param_line = params_line.match(/(\w+)/g);
  73. if (param_line && param_line.length > 2) {
  74. $.each(param_line, function(index, param) {
  75. if (index >= 2) { // Skips define NAME
  76. delete params[param];
  77. }
  78. });
  79. }
  80. });
  81. }
  82. if (macro_returns) {
  83. $.each(macro_returns, function(index, params_line) {
  84. var param_line = params_line.match(/(\w+)/g);
  85. if (param_line) {
  86. $.each(param_line, function(index, param) {
  87. if (index >= 1) { // Skip returns
  88. delete params[param];
  89. }
  90. });
  91. }
  92. });
  93. }
  94. $.each(self.parameters(), function(index, param) {
  95. params[param.name()] = param.value();
  96. });
  97. return params;
  98. };
  99. self.hadoopProperties = ko.observableArray([]);
  100. ko.utils.arrayForEach(pigScript.hadoopProperties, function (property) {
  101. self.hadoopProperties.push(new HadoopProperty({name: property.name, value: property.value}));
  102. });
  103. self.addHadoopProperties = function () {
  104. self.hadoopProperties.push(new HadoopProperty({name: '', value: ''}));
  105. self.updateParentModel();
  106. };
  107. self.removeHadoopProperties = function () {
  108. self.hadoopProperties.remove(this);
  109. self.updateParentModel();
  110. };
  111. self.resources = ko.observableArray([]);
  112. ko.utils.arrayForEach(pigScript.resources, function (resource) {
  113. self.resources.push(new Resource({type: resource.type, value: resource.value}));
  114. });
  115. self.addResource = function () {
  116. self.resources.push(new Resource({type: 'file', value: ''}));
  117. self.updateParentModel();
  118. };
  119. self.removeResource = function () {
  120. self.resources.remove(this);
  121. self.updateParentModel();
  122. };
  123. self.parentModel = pigScript.parentModel;
  124. self.updateParentModel = function () {
  125. if (typeof self.parentModel != "undefined" && self.parentModel != null) {
  126. self.parentModel.isDirty(true);
  127. }
  128. }
  129. self.name.subscribe(function (name) {
  130. self.updateParentModel();
  131. });
  132. }
  133. var Workflow = function (wf) {
  134. return {
  135. id: wf.id,
  136. scriptId: wf.scriptId,
  137. scriptContent: wf.scriptContent,
  138. lastModTime: wf.lastModTime,
  139. endTime: wf.endTime,
  140. status: wf.status,
  141. statusClass: "label " + getStatusClass(wf.status),
  142. isRunning: wf.isRunning,
  143. duration: wf.duration,
  144. appName: wf.appName,
  145. progress: wf.progress,
  146. progressPercent: wf.progressPercent,
  147. progressClass: "bar " + getStatusClass(wf.status, "bar-"),
  148. user: wf.user,
  149. absoluteUrl: wf.absoluteUrl,
  150. watchUrl: wf.watchUrl,
  151. canEdit: wf.canEdit,
  152. killUrl: wf.killUrl,
  153. created: wf.created,
  154. run: wf.run
  155. }
  156. }
  157. var PigViewModel = function (props) {
  158. var self = this;
  159. self.LABELS = props.labels;
  160. self.LIST_SCRIPTS = props.listScripts;
  161. self.SAVE_URL = props.saveUrl;
  162. self.RUN_URL = props.runUrl;
  163. self.STOP_URL = props.stopUrl;
  164. self.COPY_URL = props.copyUrl;
  165. self.DELETE_URL = props.deleteUrl;
  166. self.isLoading = ko.observable(false);
  167. self.allSelected = ko.observable(false);
  168. self.submissionVariables = ko.observableArray([]);
  169. self.scripts = ko.observableArray([]);
  170. self.filteredScripts = ko.observableArray(self.scripts());
  171. self.runningScripts = ko.observableArray([]);
  172. self.completedScripts = ko.observableArray([]);
  173. self.isDashboardLoaded = false;
  174. self.isDirty = ko.observable(false);
  175. var _defaultScript = {
  176. id: -1,
  177. docId: -1,
  178. name: self.LABELS.NEW_SCRIPT_NAME,
  179. script: self.LABELS.NEW_SCRIPT_CONTENT,
  180. parameters: self.LABELS.NEW_SCRIPT_PARAMETERS,
  181. resources: self.LABELS.NEW_SCRIPT_RESOURCES,
  182. hadoopProperties: self.LABELS.NEW_SCRIPT_HADOOP_PROPERTIES,
  183. parentModel: self,
  184. can_write: true
  185. };
  186. self.currentScript = ko.observable(new PigScript(_defaultScript));
  187. self.loadingScript = null;
  188. self.currentDeleteType = ko.observable("");
  189. self.selectedScripts = ko.computed(function () {
  190. return ko.utils.arrayFilter(self.scripts(), function (script) {
  191. return script.selected();
  192. });
  193. }, self);
  194. self.selectedScript = ko.computed(function () {
  195. return self.selectedScripts()[0];
  196. }, self);
  197. self.selectAll = function () {
  198. self.allSelected(! self.allSelected());
  199. ko.utils.arrayForEach(self.scripts(), function (script) {
  200. script.selected(self.allSelected());
  201. });
  202. return true;
  203. };
  204. self.getScriptById = function (id) {
  205. var _s = null;
  206. ko.utils.arrayForEach(self.scripts(), function (script) {
  207. if (script.id() == id) {
  208. _s = script;
  209. }
  210. });
  211. return _s;
  212. }
  213. self.filterScripts = function (filter) {
  214. self.filteredScripts(ko.utils.arrayFilter(self.scripts(), function (script) {
  215. return script.isDesign() && script.name().toLowerCase().indexOf(filter.toLowerCase()) > -1
  216. }));
  217. };
  218. self.loadScript = function (id) {
  219. var _s = self.getScriptById(id);
  220. if (_s != null) {
  221. self.currentScript(_s);
  222. }
  223. else {
  224. self.currentScript(new PigScript(_defaultScript));
  225. }
  226. }
  227. self.confirmNewScript = function () {
  228. if (self.isDirty()) {
  229. showConfirmModal();
  230. }
  231. else {
  232. self.newScript();
  233. }
  234. };
  235. self.confirmScript = function () {
  236. if (self.loadingScript != null){
  237. self.viewScript(self.loadingScript);
  238. }
  239. else {
  240. self.newScript();
  241. }
  242. };
  243. self.newScript = function () {
  244. self.loadingScript = null;
  245. self.currentScript(new PigScript(_defaultScript));
  246. self.isDirty(false);
  247. $("#confirmModal").modal("hide");
  248. $(document).trigger("loadEditor");
  249. $(document).trigger("showEditor");
  250. $(document).trigger("clearLogs");
  251. };
  252. self.editScript = function (script) {
  253. $(document).trigger("showEditor");
  254. };
  255. self.editScriptProperties = function (script) {
  256. $(document).trigger("showProperties");
  257. };
  258. self.showScriptLogs = function (script) {
  259. $(document).trigger("showLogs");
  260. };
  261. self.confirmViewScript = function (script) {
  262. if (self.isDirty()) {
  263. self.loadingScript = script;
  264. showConfirmModal();
  265. }
  266. else {
  267. self.viewScript(script);
  268. }
  269. };
  270. self.viewScript = function (script) {
  271. self.loadingScript = null;
  272. self.currentScript(script);
  273. self.isDirty(false);
  274. $("#confirmModal").modal("hide");
  275. $(document).trigger("loadEditor");
  276. $(document).trigger("showEditor");
  277. };
  278. self.saveScript = function () {
  279. if (self.LABELS.NEW_SCRIPT_NAME == self.currentScript().name()){
  280. showNameModal();
  281. }
  282. else {
  283. $("#nameModal").modal("hide");
  284. callSave(self.currentScript());
  285. self.isDirty(false);
  286. }
  287. };
  288. self.runScript = function () {
  289. callRun(self.currentScript());
  290. };
  291. self.copyScript = function () {
  292. callCopy(self.currentScript());
  293. viewModel.isDirty(true);
  294. };
  295. self.confirmDeleteScript = function () {
  296. self.currentDeleteType("single");
  297. showDeleteModal();
  298. };
  299. self.stopScript = function () {
  300. callStop(self.currentScript());
  301. };
  302. self.listRunScript = function () {
  303. self.currentScript(self.selectedScript());
  304. self.runOrShowSubmissionModal();
  305. };
  306. self.listCopyScript = function () {
  307. callCopy(self.selectedScript());
  308. };
  309. self.listConfirmDeleteScripts = function () {
  310. self.currentDeleteType("multiple");
  311. showDeleteModal();
  312. };
  313. self.deleteScripts = function () {
  314. var ids = [];
  315. if (self.currentDeleteType() == "single") {
  316. ids.push(self.currentScript().id());
  317. }
  318. if (self.currentDeleteType() == "multiple") {
  319. $(self.selectedScripts()).each(function (index, script) {
  320. ids.push(script.id());
  321. });
  322. }
  323. callDelete(ids);
  324. };
  325. self.updateScripts = function () {
  326. $.getJSON(self.LIST_SCRIPTS, function (data) {
  327. self.scripts(ko.utils.arrayMap(data, function (script) {
  328. script.parentModel = self;
  329. return new PigScript(script);
  330. }));
  331. self.filteredScripts(self.scripts());
  332. $(document).trigger("scriptsRefreshed");
  333. });
  334. };
  335. self.updateDashboard = function (workflows) {
  336. self.isDashboardLoaded = true;
  337. var koWorkflows = ko.utils.arrayMap(workflows, function (wf) {
  338. return new Workflow(wf);
  339. });
  340. self.runningScripts(ko.utils.arrayFilter(koWorkflows, function (wf) {
  341. return wf.isRunning
  342. }));
  343. self.completedScripts(ko.utils.arrayFilter(koWorkflows, function (wf) {
  344. return !wf.isRunning
  345. }));
  346. }
  347. self.runOrShowSubmissionModal = function runOrShowSubmissionModal() {
  348. var script = self.currentScript();
  349. if (! $.isEmptyObject(script.getParameters())) {
  350. self.submissionVariables.removeAll();
  351. $.each(script.getParameters(), function (key, value) {
  352. self.submissionVariables.push({'name': key, 'value': value});
  353. });
  354. $("#runScriptBtn").button("reset");
  355. $("#runScriptBtn").attr("data-loading-text", $("#runScriptBtn").text() + " ...");
  356. $("#submitModal").modal({
  357. keyboard: true,
  358. show: true
  359. });
  360. } else {
  361. self.runScript();
  362. }
  363. };
  364. self.showStopModal = function showStopModal() {
  365. $("#stopScriptBtn").button("reset");
  366. $("#stopScriptBtn").attr("data-loading-text", $("#stopScriptBtn").text() + " ...");
  367. $("#stopModal").modal({
  368. keyboard: true,
  369. show: true
  370. });
  371. }
  372. self.showFileChooser = function showFileChooser() {
  373. var inputPath = this;
  374. var path = inputPath.value().substr(0, inputPath.value().lastIndexOf("/"));
  375. $("#filechooser").jHueFileChooser({
  376. initialPath: path,
  377. onFileChoose: function (filePath) {
  378. inputPath.value(filePath);
  379. $("#chooseFile").modal("hide");
  380. },
  381. createFolder: false
  382. });
  383. $("#chooseFile").modal("show");
  384. };
  385. function showDeleteModal() {
  386. $(".deleteMsg").addClass("hide");
  387. if (self.currentDeleteType() == "single") {
  388. $(".deleteMsg.single").removeClass("hide");
  389. }
  390. if (self.currentDeleteType() == "multiple") {
  391. if (self.selectedScripts().length > 1) {
  392. $(".deleteMsg.multiple").removeClass("hide");
  393. }
  394. else {
  395. $(".deleteMsg.single").removeClass("hide");
  396. }
  397. }
  398. $("#deleteModal").modal({
  399. keyboard: true,
  400. show: true
  401. });
  402. }
  403. function showStopModal() {
  404. $(".stopMsg").addClass("hide");
  405. if (self.currentStopType() == "single") {
  406. $(".stopMsg.single").removeClass("hide");
  407. }
  408. if (self.currentStopType() == "multiple") {
  409. if (self.selectedScripts().length > 1) {
  410. $(".stopMsg.multiple").removeClass("hide");
  411. } else {
  412. $(".stopMsg.single").removeClass("hide");
  413. }
  414. }
  415. $("#stopModal").modal({
  416. keyboard: true,
  417. show: true
  418. });
  419. }
  420. function showConfirmModal() {
  421. $("#confirmModal").modal({
  422. keyboard: true,
  423. show: true
  424. });
  425. }
  426. function showNameModal() {
  427. $("#nameModal").modal({
  428. keyboard: true,
  429. show: true
  430. });
  431. }
  432. function updateScript(script, data) {
  433. script.id(data.id);
  434. script.docId(data.docId);
  435. }
  436. function callSave(script) {
  437. $(document).trigger("saving");
  438. $.post(self.SAVE_URL,
  439. {
  440. id: script.id(),
  441. name: script.name(),
  442. script: script.script(),
  443. parameters: ko.toJSON(script.parameters()),
  444. resources: ko.toJSON(script.resources()),
  445. hadoopProperties: ko.toJSON(script.hadoopProperties()),
  446. },
  447. function (data) {
  448. updateScript(self.currentScript(), data);
  449. self.updateScripts();
  450. $(document).trigger("saved");
  451. }, "json").fail( function(xhr, textStatus, errorThrown) {
  452. $(document).trigger("error", xhr.responseText);
  453. });
  454. }
  455. function callRun(script) {
  456. self.currentScript(script);
  457. $(document).trigger("clearLogs");
  458. script.isRunning(true);
  459. script.actions([]);
  460. $(document).trigger("showLogs");
  461. $(document).trigger("running");
  462. $("#submitModal").modal("hide");
  463. $.post(self.RUN_URL,
  464. {
  465. id: script.id(),
  466. name: script.name(),
  467. script: script.script(),
  468. parameters: ko.toJSON(script.parameters()),
  469. submissionVariables: ko.utils.stringifyJson(self.submissionVariables()),
  470. resources: ko.toJSON(script.resources()),
  471. hadoopProperties: ko.toJSON(script.hadoopProperties())
  472. },
  473. function (data) {
  474. if (data.id && self.currentScript().id() != data.id){
  475. updateScript(self.currentScript(), data);
  476. $(document).trigger("loadEditor");
  477. }
  478. script.isRunning(true);
  479. script.watchUrl(data.watchUrl);
  480. $(document).trigger("startLogsRefresh");
  481. self.updateScripts();
  482. }, "json").fail( function(xhr, textStatus, errorThrown) {
  483. $(document).trigger("error", xhr.responseText);
  484. });
  485. }
  486. function callStop(script) {
  487. $(document).trigger("stopping");
  488. $.post(self.STOP_URL, {
  489. id: script.id()
  490. },
  491. function (data) {
  492. $(document).trigger("stopped");
  493. $("#stopModal").modal("hide");
  494. }, "json"
  495. ).fail(function () {
  496. self.currentScript().isRunning(false);
  497. $(document).trigger("stopError");
  498. $(document).trigger("stopped");
  499. $("#stopModal").modal("hide");
  500. });
  501. }
  502. function callCopy(script) {
  503. $.post(self.COPY_URL,
  504. {
  505. id: script.id()
  506. },
  507. function (data) {
  508. data.parentModel = self;
  509. self.currentScript(new PigScript(data));
  510. $(document).trigger("loadEditor");
  511. self.updateScripts();
  512. }, "json");
  513. }
  514. function callDelete(ids) {
  515. if (ids.indexOf(self.currentScript().id()) > -1) {
  516. self.currentScript(new PigScript(_defaultScript));
  517. $(document).trigger("loadEditor");
  518. }
  519. $.post(self.DELETE_URL, {
  520. ids: ids.join(",")
  521. },
  522. function (data) {
  523. self.updateScripts();
  524. $("#deleteModal").modal("hide");
  525. viewModel.isDirty(false);
  526. }, "json");
  527. }
  528. self.viewSubmittedScript = function (workflow) {
  529. self.loadScript(workflow.scriptId);
  530. self.currentScript().script(workflow.scriptContent);
  531. self.currentScript().isRunning(true);
  532. self.currentScript().watchUrl(workflow.watchUrl);
  533. $(document).trigger("loadEditor");
  534. $(document).trigger("clearLogs");
  535. $(document).trigger("startLogsRefresh");
  536. $(document).trigger("showLogs");
  537. };
  538. self.showLogsInterval = -1;
  539. self.showLogsAtEnd = true;
  540. self.showLogs = function (workflow) {
  541. window.clearInterval(self.showLogsInterval);
  542. $("#logsModal pre").scroll(function () {
  543. self.showLogsAtEnd = $(this).scrollTop() + $(this).height() + 20 >= $(this)[0].scrollHeight;
  544. });
  545. if (workflow.isRunning) {
  546. $("#logsModal img").removeClass("hide");
  547. $("#logsModal pre").addClass("hide");
  548. $("#logsModal").modal({
  549. keyboard: true,
  550. show: true
  551. });
  552. $("#logsModal").on("hide", function () {
  553. window.clearInterval(self.showLogsInterval);
  554. });
  555. self.showLogsInterval = window.setInterval(function () {
  556. $.getJSON(workflow.watchUrl, function (data) {
  557. if (data.workflow && !data.workflow.isRunning) {
  558. window.clearInterval(self.showLogsInterval);
  559. }
  560. if (data.logs.pig) {
  561. $("#logsModal img").addClass("hide");
  562. $("#logsModal pre").removeClass("hide");
  563. var _logsEl = $("#logsModal pre");
  564. var newLines = data.logs.pig.split("\n").slice(_logsEl.html().split("<br>").length);
  565. if (newLines.length > 0){
  566. _logsEl.html(_logsEl.html() + newLines.join("<br>") + "<br>");
  567. }
  568. if (self.showLogsAtEnd) {
  569. _logsEl.scrollTop(_logsEl[0].scrollHeight - _logsEl.height());
  570. }
  571. }
  572. });
  573. }, 1000);
  574. }
  575. };
  576. };