dpgaspar commented on code in PR #22809:
URL: https://github.com/apache/superset/pull/22809#discussion_r1085516617


##########
superset/sqllab/api.py:
##########
@@ -0,0 +1,250 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import logging
+from typing import Any, cast, Dict, Optional
+
+from flask import request, Response
+from flask_appbuilder.api import expose, protect, rison, safe
+from flask_appbuilder.models.sqla.interface import SQLAInterface
+from marshmallow import ValidationError
+
+from superset import app, is_feature_enabled
+from superset.constants import MODEL_API_RW_METHOD_PERMISSION_MAP
+from superset.databases.dao import DatabaseDAO
+from superset.extensions import event_logger
+from superset.jinja_context import get_template_processor
+from superset.models.sql_lab import Query
+from superset.queries.dao import QueryDAO
+from superset.sql_lab import get_sql_results
+from superset.sqllab.command_status import SqlJsonExecutionStatus
+from superset.sqllab.commands.execute import CommandResult, ExecuteSqlCommand
+from superset.sqllab.commands.results import SqlExecutionResultsCommand
+from superset.sqllab.exceptions import (
+    QueryIsForbiddenToAccessException,
+    SqlLabException,
+)
+from superset.sqllab.execution_context_convertor import 
ExecutionContextConvertor
+from superset.sqllab.query_render import SqlQueryRenderImpl
+from superset.sqllab.schemas import (
+    ExecutePayloadSchema,
+    QueryExecutionResponseSchema,
+    sql_lab_get_results_schema,
+)
+from superset.sqllab.sql_json_executer import (
+    ASynchronousSqlJsonExecutor,
+    SqlJsonExecutor,
+    SynchronousSqlJsonExecutor,
+)
+from superset.sqllab.sqllab_execution_context import SqlJsonExecutionContext
+from superset.sqllab.validators import CanAccessQueryValidatorImpl
+from superset.views.base import handle_api_exception
+from superset.views.base_api import (
+    BaseSupersetModelRestApi,
+    requires_json,
+    statsd_metrics,
+)
+
+config = app.config
+logger = logging.getLogger(__name__)
+
+
+class SqlLabRestApi(BaseSupersetModelRestApi):
+    datamodel = SQLAInterface(Query)
+
+    include_route_methods = {
+        "get_results",
+        "execute_sql_query",
+    }
+    resource_name = "sqllab"
+    allow_browser_login = True
+
+    class_permission_name = "Query"
+    method_permission_name = MODEL_API_RW_METHOD_PERMISSION_MAP
+
+    execute_model_schema = ExecutePayloadSchema()
+
+    apispec_parameter_schemas = {
+        "sql_lab_get_results_schema": sql_lab_get_results_schema,
+    }
+    openapi_spec_tag = "SQL Lab"
+    openapi_spec_component_schemas = (
+        ExecutePayloadSchema,
+        QueryExecutionResponseSchema,
+    )
+
+    @expose("/results/")
+    @protect()
+    @safe
+    @statsd_metrics
+    @rison(sql_lab_get_results_schema)
+    @handle_api_exception
+    @event_logger.log_this_with_context(
+        action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
+        f".get_results",
+        log_to_statsd=False,
+    )
+    def get_results(self, **kwargs: Any) -> Response:
+        """Gets the result of a SQL query execution
+        ---
+        get:
+          summary: >-
+            Gets the result of a SQL query execution
+          parameters:
+          - in: query
+            name: q
+            content:
+              application/json:
+                schema:
+                  $ref: '#/components/schemas/sql_lab_get_results_schema'
+          responses:
+            200:
+              description: SQL query execution result
+              content:
+                application/json:
+                  schema:
+                    $ref: '#/components/schemas/QueryExecutionResponseSchema'
+            400:
+              $ref: '#/components/responses/400'
+            401:
+              $ref: '#/components/responses/401'
+            404:
+              $ref: '#/components/responses/404'
+            422:
+              $ref: '#/components/responses/422'

Review Comment:
   don't think this one is possible



##########
superset/sqllab/api.py:
##########
@@ -0,0 +1,250 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import logging
+from typing import Any, cast, Dict, Optional
+
+from flask import request, Response
+from flask_appbuilder.api import expose, protect, rison, safe
+from flask_appbuilder.models.sqla.interface import SQLAInterface
+from marshmallow import ValidationError
+
+from superset import app, is_feature_enabled
+from superset.constants import MODEL_API_RW_METHOD_PERMISSION_MAP
+from superset.databases.dao import DatabaseDAO
+from superset.extensions import event_logger
+from superset.jinja_context import get_template_processor
+from superset.models.sql_lab import Query
+from superset.queries.dao import QueryDAO
+from superset.sql_lab import get_sql_results
+from superset.sqllab.command_status import SqlJsonExecutionStatus
+from superset.sqllab.commands.execute import CommandResult, ExecuteSqlCommand
+from superset.sqllab.commands.results import SqlExecutionResultsCommand
+from superset.sqllab.exceptions import (
+    QueryIsForbiddenToAccessException,
+    SqlLabException,
+)
+from superset.sqllab.execution_context_convertor import 
ExecutionContextConvertor
+from superset.sqllab.query_render import SqlQueryRenderImpl
+from superset.sqllab.schemas import (
+    ExecutePayloadSchema,
+    QueryExecutionResponseSchema,
+    sql_lab_get_results_schema,
+)
+from superset.sqllab.sql_json_executer import (
+    ASynchronousSqlJsonExecutor,
+    SqlJsonExecutor,
+    SynchronousSqlJsonExecutor,
+)
+from superset.sqllab.sqllab_execution_context import SqlJsonExecutionContext
+from superset.sqllab.validators import CanAccessQueryValidatorImpl
+from superset.views.base import handle_api_exception
+from superset.views.base_api import (
+    BaseSupersetModelRestApi,
+    requires_json,
+    statsd_metrics,
+)
+
+config = app.config
+logger = logging.getLogger(__name__)
+
+
+class SqlLabRestApi(BaseSupersetModelRestApi):
+    datamodel = SQLAInterface(Query)
+
+    include_route_methods = {
+        "get_results",
+        "execute_sql_query",
+    }
+    resource_name = "sqllab"
+    allow_browser_login = True
+
+    class_permission_name = "Query"
+    method_permission_name = MODEL_API_RW_METHOD_PERMISSION_MAP
+
+    execute_model_schema = ExecutePayloadSchema()
+
+    apispec_parameter_schemas = {
+        "sql_lab_get_results_schema": sql_lab_get_results_schema,
+    }
+    openapi_spec_tag = "SQL Lab"
+    openapi_spec_component_schemas = (
+        ExecutePayloadSchema,
+        QueryExecutionResponseSchema,
+    )
+
+    @expose("/results/")
+    @protect()
+    @safe
+    @statsd_metrics
+    @rison(sql_lab_get_results_schema)
+    @handle_api_exception

Review Comment:
   do we need this one? isn't safe enough?



##########
superset/sqllab/api.py:
##########
@@ -0,0 +1,250 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import logging
+from typing import Any, cast, Dict, Optional
+
+from flask import request, Response
+from flask_appbuilder.api import expose, protect, rison, safe
+from flask_appbuilder.models.sqla.interface import SQLAInterface
+from marshmallow import ValidationError
+
+from superset import app, is_feature_enabled
+from superset.constants import MODEL_API_RW_METHOD_PERMISSION_MAP
+from superset.databases.dao import DatabaseDAO
+from superset.extensions import event_logger
+from superset.jinja_context import get_template_processor
+from superset.models.sql_lab import Query
+from superset.queries.dao import QueryDAO
+from superset.sql_lab import get_sql_results
+from superset.sqllab.command_status import SqlJsonExecutionStatus
+from superset.sqllab.commands.execute import CommandResult, ExecuteSqlCommand
+from superset.sqllab.commands.results import SqlExecutionResultsCommand
+from superset.sqllab.exceptions import (
+    QueryIsForbiddenToAccessException,
+    SqlLabException,
+)
+from superset.sqllab.execution_context_convertor import 
ExecutionContextConvertor
+from superset.sqllab.query_render import SqlQueryRenderImpl
+from superset.sqllab.schemas import (
+    ExecutePayloadSchema,
+    QueryExecutionResponseSchema,
+    sql_lab_get_results_schema,
+)
+from superset.sqllab.sql_json_executer import (
+    ASynchronousSqlJsonExecutor,
+    SqlJsonExecutor,
+    SynchronousSqlJsonExecutor,
+)
+from superset.sqllab.sqllab_execution_context import SqlJsonExecutionContext
+from superset.sqllab.validators import CanAccessQueryValidatorImpl
+from superset.views.base import handle_api_exception
+from superset.views.base_api import (
+    BaseSupersetModelRestApi,
+    requires_json,
+    statsd_metrics,
+)
+
+config = app.config
+logger = logging.getLogger(__name__)
+
+
+class SqlLabRestApi(BaseSupersetModelRestApi):
+    datamodel = SQLAInterface(Query)
+
+    include_route_methods = {
+        "get_results",
+        "execute_sql_query",
+    }
+    resource_name = "sqllab"
+    allow_browser_login = True
+
+    class_permission_name = "Query"
+    method_permission_name = MODEL_API_RW_METHOD_PERMISSION_MAP
+
+    execute_model_schema = ExecutePayloadSchema()
+
+    apispec_parameter_schemas = {
+        "sql_lab_get_results_schema": sql_lab_get_results_schema,
+    }
+    openapi_spec_tag = "SQL Lab"
+    openapi_spec_component_schemas = (
+        ExecutePayloadSchema,
+        QueryExecutionResponseSchema,
+    )
+
+    @expose("/results/")
+    @protect()
+    @safe
+    @statsd_metrics
+    @rison(sql_lab_get_results_schema)
+    @handle_api_exception
+    @event_logger.log_this_with_context(
+        action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
+        f".get_results",
+        log_to_statsd=False,
+    )
+    def get_results(self, **kwargs: Any) -> Response:
+        """Gets the result of a SQL query execution
+        ---
+        get:
+          summary: >-
+            Gets the result of a SQL query execution
+          parameters:
+          - in: query
+            name: q
+            content:
+              application/json:
+                schema:
+                  $ref: '#/components/schemas/sql_lab_get_results_schema'
+          responses:
+            200:
+              description: SQL query execution result
+              content:
+                application/json:
+                  schema:
+                    $ref: '#/components/schemas/QueryExecutionResponseSchema'
+            400:
+              $ref: '#/components/responses/400'
+            401:
+              $ref: '#/components/responses/401'
+            404:
+              $ref: '#/components/responses/404'
+            422:
+              $ref: '#/components/responses/422'
+            500:
+              $ref: '#/components/responses/500'
+        """
+        params = kwargs["rison"]
+        key = params.get("key")
+        rows = params.get("rows")
+        result = SqlExecutionResultsCommand(key=key, rows=rows).run()
+        return self.response(200, **result)
+
+    @expose("/execute/", methods=["POST"])
+    @protect()
+    @statsd_metrics
+    @requires_json
+    @handle_api_exception
+    @event_logger.log_this_with_context(
+        action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
+        f".get_results",
+        log_to_statsd=False,
+    )
+    def execute_sql_query(self) -> Response:
+        """Executes a SQL query
+        ---
+        post:
+          description: >-
+            Starts the execution of a SQL query
+          requestBody:
+            description: SQL query and params
+            required: true
+            content:
+              application/json:
+                schema:
+                  $ref: '#/components/schemas/ExecutePayloadSchema'
+          responses:
+            200:
+              description: Query execution result
+              content:
+                application/json:
+                  schema:
+                    $ref: '#/components/schemas/QueryExecutionResponseSchema'
+            202:
+              description: Query execution result, query still running
+              content:
+                application/json:
+                  schema:
+                    $ref: '#/components/schemas/QueryExecutionResponseSchema'
+            400:
+              $ref: '#/components/responses/400'
+            401:
+              $ref: '#/components/responses/401'
+            404:
+              $ref: '#/components/responses/404'

Review Comment:
   don't think this one is possible



##########
superset/sqllab/api.py:
##########
@@ -0,0 +1,250 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import logging
+from typing import Any, cast, Dict, Optional
+
+from flask import request, Response
+from flask_appbuilder.api import expose, protect, rison, safe
+from flask_appbuilder.models.sqla.interface import SQLAInterface
+from marshmallow import ValidationError
+
+from superset import app, is_feature_enabled
+from superset.constants import MODEL_API_RW_METHOD_PERMISSION_MAP
+from superset.databases.dao import DatabaseDAO
+from superset.extensions import event_logger
+from superset.jinja_context import get_template_processor
+from superset.models.sql_lab import Query
+from superset.queries.dao import QueryDAO
+from superset.sql_lab import get_sql_results
+from superset.sqllab.command_status import SqlJsonExecutionStatus
+from superset.sqllab.commands.execute import CommandResult, ExecuteSqlCommand
+from superset.sqllab.commands.results import SqlExecutionResultsCommand
+from superset.sqllab.exceptions import (
+    QueryIsForbiddenToAccessException,
+    SqlLabException,
+)
+from superset.sqllab.execution_context_convertor import 
ExecutionContextConvertor
+from superset.sqllab.query_render import SqlQueryRenderImpl
+from superset.sqllab.schemas import (
+    ExecutePayloadSchema,
+    QueryExecutionResponseSchema,
+    sql_lab_get_results_schema,
+)
+from superset.sqllab.sql_json_executer import (
+    ASynchronousSqlJsonExecutor,
+    SqlJsonExecutor,
+    SynchronousSqlJsonExecutor,
+)
+from superset.sqllab.sqllab_execution_context import SqlJsonExecutionContext
+from superset.sqllab.validators import CanAccessQueryValidatorImpl
+from superset.views.base import handle_api_exception
+from superset.views.base_api import (
+    BaseSupersetModelRestApi,
+    requires_json,
+    statsd_metrics,
+)
+
+config = app.config
+logger = logging.getLogger(__name__)
+
+
+class SqlLabRestApi(BaseSupersetModelRestApi):
+    datamodel = SQLAInterface(Query)
+
+    include_route_methods = {
+        "get_results",
+        "execute_sql_query",
+    }
+    resource_name = "sqllab"
+    allow_browser_login = True
+
+    class_permission_name = "Query"
+    method_permission_name = MODEL_API_RW_METHOD_PERMISSION_MAP
+
+    execute_model_schema = ExecutePayloadSchema()
+
+    apispec_parameter_schemas = {
+        "sql_lab_get_results_schema": sql_lab_get_results_schema,
+    }
+    openapi_spec_tag = "SQL Lab"
+    openapi_spec_component_schemas = (
+        ExecutePayloadSchema,
+        QueryExecutionResponseSchema,
+    )
+
+    @expose("/results/")
+    @protect()
+    @safe
+    @statsd_metrics
+    @rison(sql_lab_get_results_schema)
+    @handle_api_exception
+    @event_logger.log_this_with_context(
+        action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
+        f".get_results",
+        log_to_statsd=False,
+    )
+    def get_results(self, **kwargs: Any) -> Response:
+        """Gets the result of a SQL query execution
+        ---
+        get:
+          summary: >-
+            Gets the result of a SQL query execution
+          parameters:
+          - in: query
+            name: q
+            content:
+              application/json:
+                schema:
+                  $ref: '#/components/schemas/sql_lab_get_results_schema'
+          responses:
+            200:
+              description: SQL query execution result
+              content:
+                application/json:
+                  schema:
+                    $ref: '#/components/schemas/QueryExecutionResponseSchema'
+            400:
+              $ref: '#/components/responses/400'
+            401:
+              $ref: '#/components/responses/401'
+            404:
+              $ref: '#/components/responses/404'
+            422:
+              $ref: '#/components/responses/422'
+            500:
+              $ref: '#/components/responses/500'
+        """
+        params = kwargs["rison"]
+        key = params.get("key")
+        rows = params.get("rows")
+        result = SqlExecutionResultsCommand(key=key, rows=rows).run()
+        return self.response(200, **result)
+
+    @expose("/execute/", methods=["POST"])
+    @protect()
+    @statsd_metrics
+    @requires_json
+    @handle_api_exception
+    @event_logger.log_this_with_context(
+        action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
+        f".get_results",
+        log_to_statsd=False,
+    )
+    def execute_sql_query(self) -> Response:
+        """Executes a SQL query
+        ---
+        post:
+          description: >-
+            Starts the execution of a SQL query
+          requestBody:
+            description: SQL query and params
+            required: true
+            content:
+              application/json:
+                schema:
+                  $ref: '#/components/schemas/ExecutePayloadSchema'
+          responses:
+            200:
+              description: Query execution result
+              content:
+                application/json:
+                  schema:
+                    $ref: '#/components/schemas/QueryExecutionResponseSchema'
+            202:
+              description: Query execution result, query still running
+              content:
+                application/json:
+                  schema:
+                    $ref: '#/components/schemas/QueryExecutionResponseSchema'
+            400:
+              $ref: '#/components/responses/400'
+            401:
+              $ref: '#/components/responses/401'

Review Comment:
   missing 403



##########
superset/sqllab/api.py:
##########
@@ -0,0 +1,250 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import logging
+from typing import Any, cast, Dict, Optional
+
+from flask import request, Response
+from flask_appbuilder.api import expose, protect, rison, safe
+from flask_appbuilder.models.sqla.interface import SQLAInterface
+from marshmallow import ValidationError
+
+from superset import app, is_feature_enabled
+from superset.constants import MODEL_API_RW_METHOD_PERMISSION_MAP
+from superset.databases.dao import DatabaseDAO
+from superset.extensions import event_logger
+from superset.jinja_context import get_template_processor
+from superset.models.sql_lab import Query
+from superset.queries.dao import QueryDAO
+from superset.sql_lab import get_sql_results
+from superset.sqllab.command_status import SqlJsonExecutionStatus
+from superset.sqllab.commands.execute import CommandResult, ExecuteSqlCommand
+from superset.sqllab.commands.results import SqlExecutionResultsCommand
+from superset.sqllab.exceptions import (
+    QueryIsForbiddenToAccessException,
+    SqlLabException,
+)
+from superset.sqllab.execution_context_convertor import 
ExecutionContextConvertor
+from superset.sqllab.query_render import SqlQueryRenderImpl
+from superset.sqllab.schemas import (
+    ExecutePayloadSchema,
+    QueryExecutionResponseSchema,
+    sql_lab_get_results_schema,
+)
+from superset.sqllab.sql_json_executer import (
+    ASynchronousSqlJsonExecutor,
+    SqlJsonExecutor,
+    SynchronousSqlJsonExecutor,
+)
+from superset.sqllab.sqllab_execution_context import SqlJsonExecutionContext
+from superset.sqllab.validators import CanAccessQueryValidatorImpl
+from superset.views.base import handle_api_exception
+from superset.views.base_api import (
+    BaseSupersetModelRestApi,
+    requires_json,
+    statsd_metrics,
+)
+
+config = app.config
+logger = logging.getLogger(__name__)
+
+
+class SqlLabRestApi(BaseSupersetModelRestApi):
+    datamodel = SQLAInterface(Query)
+
+    include_route_methods = {
+        "get_results",
+        "execute_sql_query",
+    }
+    resource_name = "sqllab"
+    allow_browser_login = True
+
+    class_permission_name = "Query"
+    method_permission_name = MODEL_API_RW_METHOD_PERMISSION_MAP
+
+    execute_model_schema = ExecutePayloadSchema()
+
+    apispec_parameter_schemas = {
+        "sql_lab_get_results_schema": sql_lab_get_results_schema,
+    }
+    openapi_spec_tag = "SQL Lab"
+    openapi_spec_component_schemas = (
+        ExecutePayloadSchema,
+        QueryExecutionResponseSchema,
+    )
+
+    @expose("/results/")
+    @protect()
+    @safe
+    @statsd_metrics
+    @rison(sql_lab_get_results_schema)
+    @handle_api_exception
+    @event_logger.log_this_with_context(
+        action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
+        f".get_results",
+        log_to_statsd=False,
+    )
+    def get_results(self, **kwargs: Any) -> Response:
+        """Gets the result of a SQL query execution
+        ---
+        get:
+          summary: >-
+            Gets the result of a SQL query execution
+          parameters:
+          - in: query
+            name: q
+            content:
+              application/json:
+                schema:
+                  $ref: '#/components/schemas/sql_lab_get_results_schema'
+          responses:
+            200:
+              description: SQL query execution result
+              content:
+                application/json:
+                  schema:
+                    $ref: '#/components/schemas/QueryExecutionResponseSchema'
+            400:
+              $ref: '#/components/responses/400'
+            401:
+              $ref: '#/components/responses/401'
+            404:
+              $ref: '#/components/responses/404'
+            422:
+              $ref: '#/components/responses/422'
+            500:
+              $ref: '#/components/responses/500'
+        """
+        params = kwargs["rison"]
+        key = params.get("key")
+        rows = params.get("rows")
+        result = SqlExecutionResultsCommand(key=key, rows=rows).run()
+        return self.response(200, **result)
+
+    @expose("/execute/", methods=["POST"])
+    @protect()
+    @statsd_metrics
+    @requires_json
+    @handle_api_exception
+    @event_logger.log_this_with_context(
+        action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
+        f".get_results",
+        log_to_statsd=False,
+    )
+    def execute_sql_query(self) -> Response:
+        """Executes a SQL query
+        ---
+        post:
+          description: >-
+            Starts the execution of a SQL query
+          requestBody:
+            description: SQL query and params
+            required: true
+            content:
+              application/json:
+                schema:
+                  $ref: '#/components/schemas/ExecutePayloadSchema'
+          responses:
+            200:
+              description: Query execution result
+              content:
+                application/json:
+                  schema:
+                    $ref: '#/components/schemas/QueryExecutionResponseSchema'
+            202:
+              description: Query execution result, query still running
+              content:
+                application/json:
+                  schema:
+                    $ref: '#/components/schemas/QueryExecutionResponseSchema'
+            400:
+              $ref: '#/components/responses/400'
+            401:
+              $ref: '#/components/responses/401'
+            404:
+              $ref: '#/components/responses/404'
+            422:
+              $ref: '#/components/responses/422'

Review Comment:
   don't think this one is possible



##########
superset/sqllab/api.py:
##########
@@ -0,0 +1,250 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import logging
+from typing import Any, cast, Dict, Optional
+
+from flask import request, Response
+from flask_appbuilder.api import expose, protect, rison, safe
+from flask_appbuilder.models.sqla.interface import SQLAInterface
+from marshmallow import ValidationError
+
+from superset import app, is_feature_enabled
+from superset.constants import MODEL_API_RW_METHOD_PERMISSION_MAP
+from superset.databases.dao import DatabaseDAO
+from superset.extensions import event_logger
+from superset.jinja_context import get_template_processor
+from superset.models.sql_lab import Query
+from superset.queries.dao import QueryDAO
+from superset.sql_lab import get_sql_results
+from superset.sqllab.command_status import SqlJsonExecutionStatus
+from superset.sqllab.commands.execute import CommandResult, ExecuteSqlCommand
+from superset.sqllab.commands.results import SqlExecutionResultsCommand
+from superset.sqllab.exceptions import (
+    QueryIsForbiddenToAccessException,
+    SqlLabException,
+)
+from superset.sqllab.execution_context_convertor import 
ExecutionContextConvertor
+from superset.sqllab.query_render import SqlQueryRenderImpl
+from superset.sqllab.schemas import (
+    ExecutePayloadSchema,
+    QueryExecutionResponseSchema,
+    sql_lab_get_results_schema,
+)
+from superset.sqllab.sql_json_executer import (
+    ASynchronousSqlJsonExecutor,
+    SqlJsonExecutor,
+    SynchronousSqlJsonExecutor,
+)
+from superset.sqllab.sqllab_execution_context import SqlJsonExecutionContext
+from superset.sqllab.validators import CanAccessQueryValidatorImpl
+from superset.views.base import handle_api_exception
+from superset.views.base_api import (
+    BaseSupersetModelRestApi,
+    requires_json,
+    statsd_metrics,
+)
+
+config = app.config
+logger = logging.getLogger(__name__)
+
+
+class SqlLabRestApi(BaseSupersetModelRestApi):
+    datamodel = SQLAInterface(Query)
+
+    include_route_methods = {
+        "get_results",
+        "execute_sql_query",
+    }
+    resource_name = "sqllab"
+    allow_browser_login = True
+
+    class_permission_name = "Query"
+    method_permission_name = MODEL_API_RW_METHOD_PERMISSION_MAP
+
+    execute_model_schema = ExecutePayloadSchema()
+
+    apispec_parameter_schemas = {
+        "sql_lab_get_results_schema": sql_lab_get_results_schema,
+    }
+    openapi_spec_tag = "SQL Lab"
+    openapi_spec_component_schemas = (
+        ExecutePayloadSchema,
+        QueryExecutionResponseSchema,
+    )
+
+    @expose("/results/")
+    @protect()
+    @safe
+    @statsd_metrics
+    @rison(sql_lab_get_results_schema)
+    @handle_api_exception
+    @event_logger.log_this_with_context(
+        action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
+        f".get_results",
+        log_to_statsd=False,
+    )
+    def get_results(self, **kwargs: Any) -> Response:
+        """Gets the result of a SQL query execution
+        ---
+        get:
+          summary: >-
+            Gets the result of a SQL query execution
+          parameters:
+          - in: query
+            name: q
+            content:
+              application/json:
+                schema:
+                  $ref: '#/components/schemas/sql_lab_get_results_schema'
+          responses:
+            200:
+              description: SQL query execution result
+              content:
+                application/json:
+                  schema:
+                    $ref: '#/components/schemas/QueryExecutionResponseSchema'
+            400:
+              $ref: '#/components/responses/400'

Review Comment:
   missing HTTP 410



##########
tests/integration_tests/sql_lab/api_tests.py:
##########
@@ -0,0 +1,173 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# isort:skip_file
+"""Unit tests for Superset"""
+import datetime
+import json
+import random
+
+import pytest
+import prison
+from sqlalchemy.sql import func
+from unittest import mock
+
+from tests.integration_tests.test_app import app
+from superset import sql_lab
+from superset.common.db_query_status import QueryStatus
+from superset.models.core import Database
+from superset.utils.database import get_example_database, get_main_database
+from superset.utils import core as utils
+from superset.models.sql_lab import Query
+
+from tests.integration_tests.base_tests import SupersetTestCase
+
+QUERIES_FIXTURE_COUNT = 10
+
+
+class TestSqlLabApi(SupersetTestCase):
+    
@mock.patch("superset.sqllab.commands.results.results_backend_use_msgpack", 
False)
+    def test_execute_required_params(self):
+        self.login()
+        client_id = "{}".format(random.getrandbits(64))[:10]
+
+        data = {"client_id": client_id}
+        rv = self.client.post(
+            "/api/v1/sqllab/execute/",
+            json=data,
+        )
+        failed_resp = {
+            "message": {
+                "sql": ["Missing data for required field."],
+                "database_id": ["Missing data for required field."],
+            }
+        }
+        resp_data = json.loads(rv.data.decode("utf-8"))
+        self.assertDictEqual(resp_data, failed_resp)
+        self.assertEqual(rv.status_code, 400)
+
+        data = {"sql": "SELECT 1", "client_id": client_id}
+        rv = self.client.post(
+            "/api/v1/sqllab/execute/",
+            json=data,
+        )
+        failed_resp = {"message": {"database_id": ["Missing data for required 
field."]}}
+        resp_data = json.loads(rv.data.decode("utf-8"))
+        self.assertDictEqual(resp_data, failed_resp)
+        self.assertEqual(rv.status_code, 400)
+
+        data = {"database_id": 1, "client_id": client_id}
+        rv = self.client.post(
+            "/api/v1/sqllab/execute/",
+            json=data,
+        )
+        failed_resp = {"message": {"sql": ["Missing data for required 
field."]}}
+        resp_data = json.loads(rv.data.decode("utf-8"))
+        self.assertDictEqual(resp_data, failed_resp)
+        self.assertEqual(rv.status_code, 400)
+
+        from superset import sql_lab as core
+
+        core.results_backend = mock.Mock()
+        core.results_backend.get.return_value = {}
+
+        data = {"sql": "SELECT 1", "database_id": 1, "client_id": client_id}
+        rv = self.client.post(
+            "/api/v1/sqllab/execute/",
+            json=data,
+        )
+        resp_data = json.loads(rv.data.decode("utf-8"))
+        self.assertEqual(resp_data.get("status"), "success")
+        self.assertEqual(rv.status_code, 200)

Review Comment:
   nit: I personally like to split 400 test cases and 200 test cases into 
separate tests



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to