diff --git a/libs/garf_core/garf_core/query_editor.py b/libs/garf_core/garf_core/query_editor.py index 0578f39..c854c93 100644 --- a/libs/garf_core/garf_core/query_editor.py +++ b/libs/garf_core/garf_core/query_editor.py @@ -53,6 +53,10 @@ class GarfResourceError(GarfQueryError): """Specifies incorrect resource name in the query.""" +class GarfBuiltInQueryError(GarfQueryError): + """Specifies non-existing builtin query.""" + + @dataclasses.dataclass class ProcessedField: """Helper class to store fields with its customizers. @@ -383,19 +387,24 @@ def macros(self) -> dict[str, str]: return common_params def generate(self) -> BaseQueryElements: - return ( - self.remove_comments() - .expand() - .extract_resource_name() - .remove_trailing_comma() + self.remove_comments().expand().extract_resource_name() + if self.query.resource_name.startswith('builtin'): + return BaseQueryElements( + title=self.query.resource_name.replace('builtin.', ''), + text=self.query.text, + resource_name=self.query.resource_name, + is_builtin_query=True, + ) + ( + self.remove_trailing_comma() .extract_fields() .extract_filters() .extract_sorts() .extract_column_names() .extract_virtual_columns() .extract_customizers() - .query ) + return self.query def expand(self) -> Self: """Applies necessary transformations to query.""" diff --git a/libs/garf_core/garf_core/report_fetcher.py b/libs/garf_core/garf_core/report_fetcher.py index 04a6c62..ddd56ac 100644 --- a/libs/garf_core/garf_core/report_fetcher.py +++ b/libs/garf_core/garf_core/report_fetcher.py @@ -11,17 +11,19 @@ # 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. + +# pylint: disable=C0330, g-bad-import-order, g-multiple-import + """Module for getting data from API based on a query. ApiReportFetcher performs fetching data from API, parsing it and returning GarfReport. """ -# pylint: disable=C0330, g-bad-import-order, g-multiple-import from __future__ import annotations import logging -from typing import Any +from typing import Any, Callable from garf_core import ( api_clients, @@ -49,6 +51,8 @@ def __init__( query_specification_builder: query_editor.QuerySpecification = ( query_editor.QuerySpecification ), + builtin_queries: dict[str, Callable[[ApiReportFetcher], report.GarfReport]] + | None = None, **kwargs: str, ) -> None: """Instantiates ApiReportFetcher based on provided api client. @@ -57,11 +61,21 @@ def __init__( api_client: Instantiated api client. parser: Type of parser to convert API response. query_specification_builder: Class to perform query parsing. + builtin_queries: + Mapping between query name and function for generating GarfReport. """ self.api_client = api_client self.parser = parser() self.query_specification_builder = query_specification_builder self.query_args = kwargs + self.builtin_queries = builtin_queries or {} + + def add_builtin_queries( + self, + builtin_queries: dict[str, Callable[[ApiReportFetcher], report.GarfReport]], + ) -> None: + """Adds new built-in queries to the fetcher.""" + self.builtin_queries.update(builtin_queries) async def afetch( self, @@ -107,6 +121,13 @@ def fetch( args=args, ) query = query_specification.generate() + if query.is_builtin_query: + if not (builtin_report := self.builtin_queries.get(query.title)): + raise query_editor.GarfBuiltInQueryError( + f'Cannot find the built-in query "{query.title}"' + ) + return builtin_report(self, **kwargs) + response = self.api_client.get_response(query, **kwargs) parsed_response = self.parser.parse_response(response, query) return report.GarfReport( diff --git a/libs/garf_core/tests/unit/test_query_editor.py b/libs/garf_core/tests/unit/test_query_editor.py index 20825ee..6be83b9 100644 --- a/libs/garf_core/tests/unit/test_query_editor.py +++ b/libs/garf_core/tests/unit/test_query_editor.py @@ -90,3 +90,16 @@ def test_generate_returns_correct(self, query): ) assert test_query_spec.query == expected_query_elements + + def test_generate_returns_builtin_query(self): + query = 'SELECT test FROM builtin.test' + test_query_spec = query_editor.QuerySpecification(text=query, title='test') + test_query_spec.generate() + expected_query_elements = query_editor.BaseQueryElements( + title='test', + text=query, + resource_name='builtin.test', + is_builtin_query=True, + ) + + assert test_query_spec.query == expected_query_elements diff --git a/libs/garf_core/tests/unit/test_report_fetcher.py b/libs/garf_core/tests/unit/test_report_fetcher.py index 96dd1d4..2c5684c 100644 --- a/libs/garf_core/tests/unit/test_report_fetcher.py +++ b/libs/garf_core/tests/unit/test_report_fetcher.py @@ -68,3 +68,18 @@ def test_fetch_returns_correct_report_for_dict_parser( ) assert test_report == expected_report + + def test_fetch_builtin_query_returns_correct_builtin_report( + self, test_dict_report_fetcher + ): + test_report = report.GarfReport(results=[[1]], column_names=['test']) + + def test_builtin_query(report_fetcher): + return test_report + + test_dict_report_fetcher.add_builtin_queries({'test': test_builtin_query}) + + query = 'SELECT test FROM builtin.test' + fetched_report = test_dict_report_fetcher.fetch(query, None) + + assert fetched_report == test_report