datasource.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. # Licensed to the Apache Software Foundation (ASF) under one
  2. # or more contributor license agreements. See the NOTICE file
  3. # distributed with this work for additional information
  4. # regarding copyright ownership. The ASF 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,
  12. # software distributed under the License is distributed on an
  13. # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. # KIND, either express or implied. See the License for the
  15. # specific language governing permissions and limitations
  16. # under the License.
  17. import json
  18. from collections import Counter
  19. from flask import request, Response
  20. from flask_appbuilder import expose
  21. from flask_appbuilder.security.decorators import has_access_api
  22. from sqlalchemy.orm.exc import NoResultFound
  23. from superset import db
  24. from superset.connectors.connector_registry import ConnectorRegistry
  25. from superset.models.core import Database
  26. from .base import api, BaseSupersetView, handle_api_exception, json_error_response
  27. class Datasource(BaseSupersetView):
  28. """Datasource-related views"""
  29. @expose("/save/", methods=["POST"])
  30. @has_access_api
  31. @api
  32. @handle_api_exception
  33. def save(self) -> Response:
  34. data = request.form.get("data")
  35. if not isinstance(data, str):
  36. return json_error_response("Request missing data field.", status="500")
  37. datasource = json.loads(data)
  38. datasource_id = datasource.get("id")
  39. datasource_type = datasource.get("type")
  40. orm_datasource = ConnectorRegistry.get_datasource(
  41. datasource_type, datasource_id, db.session
  42. )
  43. if "owners" in datasource and orm_datasource.owner_class is not None:
  44. datasource["owners"] = (
  45. db.session.query(orm_datasource.owner_class)
  46. .filter(orm_datasource.owner_class.id.in_(datasource["owners"]))
  47. .all()
  48. )
  49. duplicates = [
  50. name
  51. for name, count in Counter(
  52. [col["column_name"] for col in datasource["columns"]]
  53. ).items()
  54. if count > 1
  55. ]
  56. if duplicates:
  57. return json_error_response(
  58. f"Duplicate column name(s): {','.join(duplicates)}", status="409"
  59. )
  60. orm_datasource.update_from_object(datasource)
  61. data = orm_datasource.data
  62. db.session.commit()
  63. return self.json_response(data)
  64. @expose("/get/<datasource_type>/<datasource_id>/")
  65. @has_access_api
  66. @api
  67. @handle_api_exception
  68. def get(self, datasource_type: str, datasource_id: int) -> Response:
  69. try:
  70. orm_datasource = ConnectorRegistry.get_datasource(
  71. datasource_type, datasource_id, db.session
  72. )
  73. if not orm_datasource.data:
  74. return json_error_response(
  75. "Error fetching datasource data.", status="500"
  76. )
  77. return self.json_response(orm_datasource.data)
  78. except NoResultFound:
  79. return json_error_response("This datasource does not exist", status="400")
  80. @expose("/external_metadata/<datasource_type>/<datasource_id>/")
  81. @has_access_api
  82. @api
  83. @handle_api_exception
  84. def external_metadata(self, datasource_type: str, datasource_id: int) -> Response:
  85. """Gets column info from the source system"""
  86. if datasource_type == "druid":
  87. datasource = ConnectorRegistry.get_datasource(
  88. datasource_type, datasource_id, db.session
  89. )
  90. elif datasource_type == "table":
  91. database = (
  92. db.session.query(Database).filter_by(id=request.args.get("db_id")).one()
  93. )
  94. table_class = ConnectorRegistry.sources["table"]
  95. datasource = table_class(
  96. database=database,
  97. table_name=request.args.get("table_name"),
  98. schema=request.args.get("schema") or None,
  99. )
  100. else:
  101. raise Exception(f"Unsupported datasource_type: {datasource_type}")
  102. external_metadata = datasource.external_metadata()
  103. return self.json_response(external_metadata)