listings_handler.py 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. """Tornado handlers for listing extensions."""
  2. # Copyright (c) Jupyter Development Team.
  3. # Distributed under the terms of the Modified BSD License.
  4. from __future__ import annotations
  5. import json
  6. from logging import Logger
  7. import requests
  8. import tornado
  9. from jupyter_server.base.handlers import APIHandler
  10. LISTINGS_URL_SUFFIX = "@jupyterlab/extensionmanager-extension/listings.json"
  11. def fetch_listings(logger: Logger | None) -> None:
  12. """Fetch the listings for the extension manager."""
  13. if not logger:
  14. from traitlets import log
  15. logger = log.get_logger() # type:ignore[assignment]
  16. assert logger is not None
  17. if len(ListingsHandler.blocked_extensions_uris) > 0:
  18. blocked_extensions = []
  19. for blocked_extensions_uri in ListingsHandler.blocked_extensions_uris:
  20. logger.info(
  21. "Fetching blocked_extensions from %s", ListingsHandler.blocked_extensions_uris
  22. )
  23. r = requests.request(
  24. "GET", blocked_extensions_uri, **ListingsHandler.listings_request_opts
  25. )
  26. j = json.loads(r.text)
  27. for b in j["blocked_extensions"]:
  28. blocked_extensions.append(b)
  29. ListingsHandler.blocked_extensions = blocked_extensions
  30. if len(ListingsHandler.allowed_extensions_uris) > 0:
  31. allowed_extensions = []
  32. for allowed_extensions_uri in ListingsHandler.allowed_extensions_uris:
  33. logger.info(
  34. "Fetching allowed_extensions from %s", ListingsHandler.allowed_extensions_uris
  35. )
  36. r = requests.request(
  37. "GET", allowed_extensions_uri, **ListingsHandler.listings_request_opts
  38. )
  39. j = json.loads(r.text)
  40. for w in j["allowed_extensions"]:
  41. allowed_extensions.append(w)
  42. ListingsHandler.allowed_extensions = allowed_extensions
  43. ListingsHandler.listings = json.dumps( # type:ignore[attr-defined]
  44. {
  45. "blocked_extensions_uris": list(ListingsHandler.blocked_extensions_uris),
  46. "allowed_extensions_uris": list(ListingsHandler.allowed_extensions_uris),
  47. "blocked_extensions": ListingsHandler.blocked_extensions,
  48. "allowed_extensions": ListingsHandler.allowed_extensions,
  49. }
  50. )
  51. class ListingsHandler(APIHandler):
  52. """An handler that returns the listings specs."""
  53. """Below fields are class level fields that are accessed and populated
  54. by the initialization and the fetch_listings methods.
  55. Some fields are initialized before the handler creation in the
  56. handlers.py#add_handlers method.
  57. Having those fields predefined reduces the guards in the methods using
  58. them.
  59. """
  60. # The list of blocked_extensions URIS.
  61. blocked_extensions_uris: set = set()
  62. # The list of allowed_extensions URIS.
  63. allowed_extensions_uris: set = set()
  64. # The blocked extensions extensions.
  65. blocked_extensions: list = []
  66. # The allowed extensions extensions.
  67. allowed_extensions: list = []
  68. # The provider request options to be used for the request library.
  69. listings_request_opts: dict = {}
  70. # The callback time for the periodic callback in seconds.
  71. listings_refresh_seconds: int
  72. # The PeriodicCallback that schedule the call to fetch_listings method.
  73. pc = None
  74. @tornado.web.authenticated
  75. def get(self, path: str) -> None:
  76. """Get the listings for the extension manager."""
  77. self.set_header("Content-Type", "application/json")
  78. if path == LISTINGS_URL_SUFFIX:
  79. self.write(ListingsHandler.listings) # type:ignore[attr-defined]
  80. else:
  81. raise tornado.web.HTTPError(400)