handlers.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. #
  2. # Copyright 2018-2022 Elyra Authors
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. from jsonschema import ValidationError
  17. from jupyter_server.base.handlers import APIHandler
  18. from jupyter_server.utils import url_path_join
  19. from jupyter_server.utils import url_unescape
  20. from tornado import web
  21. from elyra.metadata.error import MetadataExistsError
  22. from elyra.metadata.error import MetadataNotFoundError
  23. from elyra.metadata.error import SchemaNotFoundError
  24. from elyra.metadata.manager import MetadataManager
  25. from elyra.metadata.metadata import Metadata
  26. from elyra.metadata.schema import SchemaManager
  27. from elyra.util.http import HttpErrorMixin
  28. class MetadataHandler(HttpErrorMixin, APIHandler):
  29. """Handler for metadata configurations collection."""
  30. @web.authenticated
  31. async def get(self, schemaspace):
  32. schemaspace = url_unescape(schemaspace)
  33. parent = self.settings.get("elyra")
  34. try:
  35. metadata_manager = MetadataManager(schemaspace=schemaspace, parent=parent)
  36. metadata = metadata_manager.get_all()
  37. except (ValidationError, ValueError) as err:
  38. raise web.HTTPError(400, str(err)) from err
  39. except MetadataNotFoundError as err:
  40. raise web.HTTPError(404, str(err)) from err
  41. except Exception as err:
  42. raise web.HTTPError(500, repr(err)) from err
  43. metadata_model = {schemaspace: [r.to_dict(trim=True) for r in metadata]}
  44. self.set_header("Content-Type", "application/json")
  45. self.finish(metadata_model)
  46. @web.authenticated
  47. async def post(self, schemaspace):
  48. schemaspace = url_unescape(schemaspace)
  49. parent = self.settings.get("elyra")
  50. try:
  51. instance = self._validate_body(schemaspace)
  52. self.log.debug(
  53. f"MetadataHandler: Creating metadata instance '{instance.name}' in schemaspace '{schemaspace}'..."
  54. )
  55. metadata_manager = MetadataManager(schemaspace=schemaspace, parent=parent)
  56. metadata = metadata_manager.create(instance.name, instance)
  57. except (ValidationError, ValueError, SyntaxError) as err:
  58. raise web.HTTPError(400, str(err)) from err
  59. except (MetadataNotFoundError, SchemaNotFoundError) as err:
  60. raise web.HTTPError(404, str(err)) from err
  61. except MetadataExistsError as err:
  62. raise web.HTTPError(409, str(err)) from err
  63. except Exception as err:
  64. raise web.HTTPError(500, repr(err)) from err
  65. self.set_status(201)
  66. self.set_header("Content-Type", "application/json")
  67. location = url_path_join(self.base_url, "elyra", "metadata", schemaspace, metadata.name)
  68. self.set_header("Location", location)
  69. self.finish(metadata.to_dict(trim=True))
  70. def _validate_body(self, schemaspace: str):
  71. """Validates the body issued for creates."""
  72. body = self.get_json_body()
  73. # Ensure schema_name and metadata fields exist.
  74. required_fields = ["schema_name", "metadata"]
  75. for field in required_fields:
  76. if field not in body:
  77. raise SyntaxError(f"Insufficient information - '{field}' is missing from request body.")
  78. # Ensure there is at least one of name or a display_name
  79. one_of_fields = ["name", "display_name"]
  80. if set(body).isdisjoint(one_of_fields):
  81. raise SyntaxError(
  82. f"Insufficient information - request body requires one of the following: {one_of_fields}."
  83. )
  84. instance = Metadata.from_dict(schemaspace, {**body})
  85. return instance
  86. class MetadataResourceHandler(HttpErrorMixin, APIHandler):
  87. """Handler for metadata configuration specific resource (e.g. a runtime element)."""
  88. @web.authenticated
  89. async def get(self, schemaspace, resource):
  90. schemaspace = url_unescape(schemaspace)
  91. resource = url_unescape(resource)
  92. parent = self.settings.get("elyra")
  93. try:
  94. metadata_manager = MetadataManager(schemaspace=schemaspace, parent=parent)
  95. metadata = metadata_manager.get(resource)
  96. except (ValidationError, ValueError, NotImplementedError) as err:
  97. raise web.HTTPError(400, str(err)) from err
  98. except MetadataNotFoundError as err:
  99. raise web.HTTPError(404, str(err)) from err
  100. except Exception as err:
  101. raise web.HTTPError(500, repr(err)) from err
  102. self.set_header("Content-Type", "application/json")
  103. self.finish(metadata.to_dict(trim=True))
  104. @web.authenticated
  105. async def put(self, schemaspace, resource):
  106. schemaspace = url_unescape(schemaspace)
  107. resource = url_unescape(resource)
  108. parent = self.settings.get("elyra")
  109. try:
  110. payload = self.get_json_body()
  111. # Get the current resource to ensure its pre-existence
  112. metadata_manager = MetadataManager(schemaspace=schemaspace, parent=parent)
  113. metadata_manager.get(resource)
  114. # Check if name is in the payload and varies from resource, if so, raise 400
  115. if "name" in payload and payload["name"] != resource:
  116. raise NotImplementedError(
  117. f"The attempt to rename instance '{resource}' to '{payload['name']}' is not supported."
  118. )
  119. instance = Metadata.from_dict(schemaspace, {**payload})
  120. self.log.debug(
  121. f"MetadataHandler: Updating metadata instance '{resource}' in schemaspace '{schemaspace}'..."
  122. )
  123. metadata = metadata_manager.update(resource, instance)
  124. except (ValidationError, ValueError, NotImplementedError) as err:
  125. raise web.HTTPError(400, str(err)) from err
  126. except MetadataNotFoundError as err:
  127. raise web.HTTPError(404, str(err)) from err
  128. except Exception as err:
  129. raise web.HTTPError(500, repr(err)) from err
  130. self.set_status(200)
  131. self.set_header("Content-Type", "application/json")
  132. self.finish(metadata.to_dict(trim=True))
  133. @web.authenticated
  134. async def delete(self, schemaspace, resource):
  135. schemaspace = url_unescape(schemaspace)
  136. resource = url_unescape(resource)
  137. parent = self.settings.get("elyra")
  138. try:
  139. self.log.debug(
  140. f"MetadataHandler: Deleting metadata instance '{resource}' in schemaspace '{schemaspace}'..."
  141. )
  142. metadata_manager = MetadataManager(schemaspace=schemaspace, parent=parent)
  143. metadata_manager.remove(resource)
  144. except (ValidationError, ValueError) as err:
  145. raise web.HTTPError(400, str(err)) from err
  146. except PermissionError as err:
  147. raise web.HTTPError(403, str(err)) from err
  148. except MetadataNotFoundError as err:
  149. raise web.HTTPError(404, str(err)) from err
  150. except Exception as err:
  151. raise web.HTTPError(500, repr(err)) from err
  152. self.set_status(204)
  153. self.finish()
  154. class SchemaHandler(HttpErrorMixin, APIHandler):
  155. """Handler for schemaspace schemas."""
  156. @web.authenticated
  157. async def get(self, schemaspace):
  158. schemaspace = url_unescape(schemaspace)
  159. try:
  160. schema_manager = SchemaManager.instance()
  161. schemas = schema_manager.get_schemaspace_schemas(schemaspace)
  162. except (ValidationError, ValueError, SchemaNotFoundError) as err:
  163. raise web.HTTPError(404, str(err)) from err
  164. except Exception as err:
  165. raise web.HTTPError(500, repr(err)) from err
  166. schemas_model = {schemaspace: list(schemas.values())}
  167. self.set_header("Content-Type", "application/json")
  168. self.finish(schemas_model)
  169. class SchemaResourceHandler(HttpErrorMixin, APIHandler):
  170. """Handler for a specific schema (resource) for a given schemaspace."""
  171. @web.authenticated
  172. async def get(self, schemaspace, resource):
  173. schemaspace = url_unescape(schemaspace)
  174. resource = url_unescape(resource)
  175. try:
  176. schema_manager = SchemaManager.instance()
  177. schema = schema_manager.get_schema(schemaspace, resource)
  178. except (ValidationError, ValueError, SchemaNotFoundError) as err:
  179. raise web.HTTPError(404, str(err)) from err
  180. except Exception as err:
  181. raise web.HTTPError(500, repr(err)) from err
  182. self.set_header("Content-Type", "application/json")
  183. self.finish(schema)
  184. class SchemaspaceHandler(HttpErrorMixin, APIHandler):
  185. """Handler for retrieving schemaspace names."""
  186. @web.authenticated
  187. async def get(self):
  188. try:
  189. schema_manager = SchemaManager.instance()
  190. schemaspaces = schema_manager.get_schemaspace_names()
  191. except (ValidationError, ValueError) as err:
  192. raise web.HTTPError(404, str(err)) from err
  193. except Exception as err:
  194. raise web.HTTPError(500, repr(err)) from err
  195. schemaspace_model = {"schemaspaces": schemaspaces}
  196. self.set_header("Content-Type", "application/json")
  197. self.finish(schemaspace_model)
  198. class SchemaspaceResourceHandler(HttpErrorMixin, APIHandler):
  199. """Handler for retrieving schemaspace JSON info (id, display name and descripton) for a given schemaspace."""
  200. @web.authenticated
  201. async def get(self, schemaspace):
  202. try:
  203. schema_manager = SchemaManager.instance()
  204. schemaspace = schema_manager.get_schemaspace(schemaspace)
  205. except (ValidationError, ValueError) as err:
  206. raise web.HTTPError(404, str(err)) from err
  207. except Exception as err:
  208. raise web.HTTPError(500, repr(err)) from err
  209. schemaspace_info_model = {
  210. "name": schemaspace.name,
  211. "id": schemaspace.id,
  212. "display_name": schemaspace.display_name,
  213. "description": schemaspace.description,
  214. }
  215. self.set_header("Content-Type", "application/json")
  216. self.finish(schemaspace_info_model)