extension_manager_handler.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. """Tornado handlers for extension management."""
  2. # Copyright (c) Jupyter Development Team.
  3. # Distributed under the terms of the Modified BSD License.
  4. import dataclasses
  5. import json
  6. from urllib.parse import urlencode, urlunparse
  7. from jupyter_server.base.handlers import APIHandler
  8. from tornado import web
  9. from jupyterlab.extensions.manager import ExtensionManager
  10. class ExtensionHandler(APIHandler):
  11. def initialize(self, manager: ExtensionManager):
  12. super().initialize()
  13. self.manager = manager
  14. @web.authenticated
  15. async def get(self):
  16. """GET query returns info on extensions
  17. Query arguments:
  18. refresh: [optional] Force refreshing the list of extensions - ["0", "1"]; default 0
  19. query: [optional] Query to search for extensions - default None (i.e. returns installed extensions)
  20. page: [optional] Result page - default 1 (min. 1)
  21. per_page: [optional] Number of results per page - default 30 (max. 100)
  22. """
  23. query = self.get_argument("query", None)
  24. page = max(1, int(self.get_argument("page", "1")))
  25. per_page = min(100, int(self.get_argument("per_page", "30")))
  26. if self.get_argument("refresh", "0") == "1":
  27. await self.manager.refresh(query, page, per_page)
  28. extensions, last_page = await self.manager.list_extensions(query, page, per_page)
  29. self.set_status(200)
  30. if last_page is not None:
  31. links = []
  32. query_args = {"page": last_page, "per_page": per_page}
  33. if query is not None:
  34. query_args["query"] = query
  35. last = urlunparse(
  36. (
  37. self.request.protocol,
  38. self.request.host,
  39. self.request.path,
  40. "",
  41. urlencode(query_args, doseq=True),
  42. "",
  43. )
  44. )
  45. links.append(f'<{last}>; rel="last"')
  46. if page > 1:
  47. query_args["page"] = max(1, page - 1)
  48. prev = urlunparse(
  49. (
  50. self.request.protocol,
  51. self.request.host,
  52. self.request.path,
  53. "",
  54. urlencode(query_args, doseq=True),
  55. "",
  56. )
  57. )
  58. links.append(f'<{prev}>; rel="prev"')
  59. if page < last_page:
  60. query_args["page"] = min(page + 1, last_page)
  61. next_ = urlunparse(
  62. (
  63. self.request.protocol,
  64. self.request.host,
  65. self.request.path,
  66. "",
  67. urlencode(query_args, doseq=True),
  68. "",
  69. )
  70. )
  71. links.append(f'<{next_}>; rel="next"')
  72. query_args["page"] = 1
  73. first = urlunparse(
  74. (
  75. self.request.protocol,
  76. self.request.host,
  77. self.request.path,
  78. "",
  79. urlencode(query_args, doseq=True),
  80. "",
  81. )
  82. )
  83. links.append(f'<{first}>; rel="first"')
  84. self.set_header("Link", ", ".join(links))
  85. self.finish(json.dumps(list(map(dataclasses.asdict, extensions))))
  86. @web.authenticated
  87. async def post(self):
  88. """POST query performs an action on a specific extension
  89. Body arguments:
  90. {
  91. "cmd": Action to perform - ["install", "uninstall", "enable", "disable"]
  92. "extension_name": Extension name
  93. "extension_version": [optional] Extension version (used only for install action)
  94. }
  95. """
  96. data = self.get_json_body()
  97. cmd = data["cmd"]
  98. name = data["extension_name"]
  99. version = data.get("extension_version")
  100. if cmd not in ("install", "uninstall", "enable", "disable") or not name:
  101. raise web.HTTPError(
  102. 422,
  103. f"Could not process instruction {cmd!r} with extension name {name!r}",
  104. )
  105. ret_value = None
  106. try:
  107. if cmd == "install":
  108. ret_value = await self.manager.install(name, version)
  109. elif cmd == "uninstall":
  110. ret_value = await self.manager.uninstall(name)
  111. elif cmd == "enable":
  112. ret_value = await self.manager.enable(name)
  113. elif cmd == "disable":
  114. ret_value = await self.manager.disable(name)
  115. except Exception as e:
  116. raise web.HTTPError(500, str(e)) from e
  117. if ret_value.status == "error":
  118. self.set_status(500)
  119. else:
  120. self.set_status(201)
  121. self.finish(json.dumps(dataclasses.asdict(ret_value)))
  122. # The path for lab extensions handler.
  123. extensions_handler_path = r"/lab/api/extensions"