session.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. define([
  4. 'jquery',
  5. 'base/js/utils',
  6. 'services/kernels/kernel',
  7. ], function($, utils, kernel) {
  8. "use strict";
  9. /**
  10. * Session object for accessing the session REST api. The session
  11. * should be used to start kernels and then shut them down -- for
  12. * all other operations, the kernel object should be used.
  13. *
  14. * Preliminary documentation for the REST API is at
  15. * https://github.com/ipython/ipython/wiki/IPEP-16%3A-Notebook-multi-directory-dashboard-and-URL-mapping#sessions-api
  16. *
  17. * Options should include:
  18. * - notebook_path: the path (not including name) to the notebook
  19. * - kernel_name: the type of kernel (e.g. python3)
  20. * - base_url: the root url of the notebook server
  21. * - ws_url: the url to access websockets
  22. * - notebook: Notebook object
  23. *
  24. * @class Session
  25. * @param {Object} options
  26. */
  27. var Session = function (options) {
  28. this.id = null;
  29. this.notebook_model = {
  30. path: options.notebook_path
  31. };
  32. this.kernel_model = {
  33. id: null,
  34. name: options.kernel_name
  35. };
  36. this.base_url = options.base_url;
  37. this.ws_url = options.ws_url;
  38. this.session_service_url = utils.url_path_join(this.base_url, 'api/sessions');
  39. this.session_url = null;
  40. this.notebook = options.notebook;
  41. this.kernel = null;
  42. this.events = options.notebook.events;
  43. this.bind_events();
  44. };
  45. Session.prototype.bind_events = function () {
  46. var that = this;
  47. var record_status = function (evt, info) {
  48. console.log('Session: ' + evt.type + ' (' + info.session.id + ')');
  49. };
  50. this.events.on('kernel_created.Session', record_status);
  51. this.events.on('kernel_dead.Session', record_status);
  52. this.events.on('kernel_killed.Session', record_status);
  53. // if the kernel dies, then also remove the session
  54. this.events.on('kernel_dead.Kernel', function () {
  55. that.delete();
  56. });
  57. this.events.on('kernel_failed_restart.Kernel', function () {
  58. that.notebook.start_session();
  59. });
  60. };
  61. // Public REST api functions
  62. /**
  63. * GET /api/sessions
  64. *
  65. * Get a list of the current sessions.
  66. *
  67. * @function list
  68. * @param {function} [success] - function executed on ajax success
  69. * @param {function} [error] - functon executed on ajax error
  70. */
  71. Session.prototype.list = function (success, error) {
  72. utils.ajax(this.session_service_url, {
  73. processData: false,
  74. cache: false,
  75. type: "GET",
  76. dataType: "json",
  77. success: success,
  78. error: this._on_error(error)
  79. });
  80. };
  81. /**
  82. * POST /api/sessions
  83. *
  84. * Start a new session. This function can only executed once.
  85. *
  86. * @function start
  87. * @param {function} [success] - function executed on ajax success
  88. * @param {function} [error] - functon executed on ajax error
  89. */
  90. Session.prototype.start = function (success, error) {
  91. var that = this;
  92. var on_success = function (data, status, xhr) {
  93. if (that.kernel) {
  94. that.kernel.name = that.kernel_model.name;
  95. } else {
  96. var kernel_service_url = utils.url_path_join(that.base_url, "api/kernels");
  97. that.kernel = new kernel.Kernel(kernel_service_url, that.ws_url, that.kernel_model.name);
  98. }
  99. that.events.trigger('kernel_created.Session', {session: that, kernel: that.kernel});
  100. that.kernel._kernel_created(data.kernel);
  101. if (success) {
  102. success(data, status, xhr);
  103. }
  104. };
  105. var on_error = function (xhr, status, err) {
  106. that.events.trigger('kernel_dead.Session', {session: that, xhr: xhr, status: status, error: err});
  107. if (error) {
  108. error(xhr, status, err);
  109. }
  110. };
  111. utils.ajax(this.session_service_url, {
  112. processData: false,
  113. cache: false,
  114. type: "POST",
  115. data: JSON.stringify(this._get_model()),
  116. contentType: 'application/json',
  117. dataType: "json",
  118. success: this._on_success(on_success),
  119. error: this._on_error(on_error)
  120. });
  121. };
  122. /**
  123. * GET /api/sessions/[:session_id]
  124. *
  125. * Get information about a session.
  126. *
  127. * @function get_info
  128. * @param {function} [success] - function executed on ajax success
  129. * @param {function} [error] - functon executed on ajax error
  130. */
  131. Session.prototype.get_info = function (success, error) {
  132. utils.ajax(this.session_url, {
  133. processData: false,
  134. cache: false,
  135. type: "GET",
  136. dataType: "json",
  137. success: this._on_success(success),
  138. error: this._on_error(error)
  139. });
  140. };
  141. /**
  142. * PATCH /api/sessions/[:session_id]
  143. *
  144. * Rename or move a notebook. If the given name or path are
  145. * undefined, then they will not be changed.
  146. *
  147. * @function rename_notebook
  148. * @param {string} [path] - new notebook path
  149. * @param {function} [success] - function executed on ajax success
  150. * @param {function} [error] - functon executed on ajax error
  151. */
  152. Session.prototype.rename_notebook = function (path, success, error) {
  153. if (path !== undefined) {
  154. this.notebook_model.path = path;
  155. }
  156. utils.ajax(this.session_url, {
  157. processData: false,
  158. cache: false,
  159. type: "PATCH",
  160. data: JSON.stringify(this._get_model()),
  161. contentType: 'application/json',
  162. dataType: "json",
  163. success: this._on_success(success),
  164. error: this._on_error(error)
  165. });
  166. };
  167. /**
  168. * DELETE /api/sessions/[:session_id]
  169. *
  170. * Kill the kernel and shutdown the session.
  171. *
  172. * @function delete
  173. * @param {function} [success] - function executed on ajax success
  174. * @param {function} [error] - functon executed on ajax error
  175. */
  176. Session.prototype.delete = function (success, error) {
  177. if (this.kernel && this.kernel.is_connected()) {
  178. this.events.trigger('kernel_killed.Session', {session: this, kernel: this.kernel});
  179. this.kernel._kernel_dead();
  180. }
  181. utils.ajax(this.session_url, {
  182. processData: false,
  183. cache: false,
  184. type: "DELETE",
  185. dataType: "json",
  186. success: this._on_success(success),
  187. error: this._on_error(error)
  188. });
  189. };
  190. /**
  191. * Restart the session by deleting it and the starting it
  192. * fresh. If options are given, they can include any of the
  193. * following:
  194. *
  195. * - notebook_path - the path to the notebook
  196. * - kernel_name - the name (type) of the kernel
  197. *
  198. * @function restart
  199. * @param {Object} [options] - options for the new kernel
  200. * @param {function} [success] - function executed on ajax success
  201. * @param {function} [error] - functon executed on ajax error
  202. */
  203. Session.prototype.restart = function (options, success, error) {
  204. var that = this;
  205. var start = function () {
  206. if (options && options.notebook_path) {
  207. that.notebook_model.path = options.notebook_path;
  208. }
  209. if (options && options.kernel_name) {
  210. that.kernel_model.name = options.kernel_name;
  211. }
  212. that.kernel_model.id = null;
  213. that.start(success, error);
  214. };
  215. this.delete(start, start);
  216. };
  217. // Helper functions
  218. /**
  219. * Get the data model for the session, which includes the notebook path
  220. * and kernel (name and id).
  221. *
  222. * @function _get_model
  223. * @returns {Object} - the data model
  224. */
  225. Session.prototype._get_model = function () {
  226. return {
  227. path: this.notebook_model.path,
  228. type: 'notebook',
  229. name: '',
  230. kernel: this.kernel_model
  231. };
  232. };
  233. /**
  234. * Update the data model from the given JSON object, which should
  235. * have attributes of `id`, `notebook`, and/or `kernel`. If
  236. * provided, the notebook data must include name and path, and the
  237. * kernel data must include name and id.
  238. *
  239. * @function _update_model
  240. * @param {Object} data - updated data model
  241. */
  242. Session.prototype._update_model = function (data) {
  243. if (data && data.id) {
  244. this.id = data.id;
  245. this.session_url = utils.url_path_join(this.session_service_url, this.id);
  246. }
  247. if (data && data.notebook) {
  248. this.notebook_model.path = data.path;
  249. }
  250. if (data && data.kernel) {
  251. this.kernel_model.name = data.kernel.name;
  252. this.kernel_model.id = data.kernel.id;
  253. }
  254. };
  255. /**
  256. * Handle a successful AJAX request by updating the session data
  257. * model with the response, and then optionally calling a provided
  258. * callback.
  259. *
  260. * @function _on_success
  261. * @param {function} success - callback
  262. */
  263. Session.prototype._on_success = function (success) {
  264. var that = this;
  265. return function (data, status, xhr) {
  266. that._update_model(data);
  267. if (success) {
  268. success(data, status, xhr);
  269. }
  270. };
  271. };
  272. /**
  273. * Handle a failed AJAX request by logging the error message, and
  274. * then optionally calling a provided callback.
  275. *
  276. * @function _on_error
  277. * @param {function} error - callback
  278. */
  279. Session.prototype._on_error = function (error) {
  280. return function (xhr, status, err) {
  281. utils.log_ajax_error(xhr, status, err);
  282. if (error) {
  283. error(xhr, status, err);
  284. }
  285. };
  286. };
  287. /**
  288. * Error type indicating that the session is already starting.
  289. */
  290. var SessionAlreadyStarting = function (message) {
  291. this.name = "SessionAlreadyStarting";
  292. this.message = (message || "");
  293. };
  294. SessionAlreadyStarting.prototype = Error.prototype;
  295. return {
  296. Session: Session,
  297. SessionAlreadyStarting: SessionAlreadyStarting
  298. };
  299. });