1616from dipdup .config import HasuraConfig , HTTPConfig , PostgresDatabaseConfig
1717from dipdup .exceptions import ConfigurationError
1818from dipdup .http import HTTPGateway
19- from dipdup .utils import iter_files , iter_models , pascal_to_snake
19+ from dipdup .utils import iter_files , iter_models , pascal_to_snake , remove_prefix
2020
2121_get_fields_query = '''
2222query introspectionQuery($name: String!) {
4747@dataclass
4848class Field :
4949 name : str
50- type : Optional [str ]
50+ type : Optional [str ] = None
5151
5252 def camelize (self ) -> 'Field' :
5353 return Field (
@@ -57,8 +57,8 @@ def camelize(self) -> 'Field':
5757
5858 @property
5959 def root (self ) -> str :
60- # NOTE: Hasura omits default schema name in root field name
61- return humps .decamelize (self .name ). lstrip ( 'public_ ' )
60+ # NOTE: Hasura omits schema name prefix in root fields
61+ return remove_prefix ( humps .decamelize (self .name ), 'public ' )
6262
6363
6464class HasuraError (RuntimeError ):
@@ -94,16 +94,15 @@ async def configure(self) -> None:
9494 metadata = await self ._fetch_metadata ()
9595
9696 # NOTE: Hasura metadata updated in three steps.
97- # NOTE: Order matters because queries must be generated after applying camelcase to model names .
97+ # NOTE: Order matters because queries must be generated after applying table customization to be valid .
9898 # NOTE: 1. Generate and apply tables metadata.
9999 source_tables_metadata = await self ._generate_source_tables_metadata ()
100100 metadata ['sources' ][0 ]['tables' ] = source_tables_metadata
101101 await self ._replace_metadata (metadata )
102102
103- # NOTE: 2. Apply camelcase and refresh metadata
104- if self ._hasura_config .camel_case :
105- await self ._apply_camelcase ()
106- metadata = await self ._fetch_metadata ()
103+ # NOTE: 2. Apply table customization and refresh metadata
104+ await self ._apply_table_customization ()
105+ metadata = await self ._fetch_metadata ()
107106
108107 # NOTE: 3. Generate and apply queries and rest endpoints
109108 query_collections_metadata = await self ._generate_query_collections_metadata ()
@@ -150,6 +149,17 @@ async def _healthcheck(self) -> None:
150149 else :
151150 raise HasuraError (f'Hasura instance not responding for { self ._hasura_config .connection_timeout } seconds' )
152151
152+ version_json = await (
153+ await self ._http ._session .get (
154+ f'{ self ._hasura_config .url } /v1/version' ,
155+ )
156+ ).json ()
157+ version = version_json ['version' ]
158+ if version .startswith ('v1' ):
159+ raise HasuraError ('Hasura v1 is not supported.' )
160+
161+ self ._logger .info ('Connected to Hasura %s' , version )
162+
153163 async def _reset_metadata (self ) -> None :
154164 self ._logger .info ('Resetting metadata' )
155165 await self ._hasura_request (
@@ -191,6 +201,13 @@ async def _get_views(self) -> List[str]:
191201 self ._logger .info ('Found %s regular and materialized views' , len (views ))
192202 return views
193203
204+ def _iterate_graphql_queries (self ) -> Iterator [Tuple [str , str ]]:
205+ package = importlib .import_module (self ._package )
206+ package_path = dirname (package .__file__ )
207+ graphql_path = join (package_path , 'graphql' )
208+ for file in iter_files (graphql_path , '.graphql' ):
209+ yield file .name .split ('/' )[- 1 ][:- 8 ], file .read ()
210+
194211 async def _generate_source_tables_metadata (self ) -> List [Dict [str , Any ]]:
195212 """Generate source tables metadata based on project models and views.
196213
@@ -235,13 +252,6 @@ async def _generate_source_tables_metadata(self) -> List[Dict[str, Any]]:
235252
236253 return list (metadata_tables .values ())
237254
238- def _iterate_graphql_queries (self ) -> Iterator [Tuple [str , str ]]:
239- package = importlib .import_module (self ._package )
240- package_path = dirname (package .__file__ )
241- graphql_path = join (package_path , 'graphql' )
242- for file in iter_files (graphql_path , '.graphql' ):
243- yield file .name .split ('/' )[- 1 ][:- 8 ], file .read ()
244-
245255 async def _generate_query_collections_metadata (self ) -> List [Dict [str , Any ]]:
246256 queries = []
247257 for _ , model in iter_models (self ._package ):
@@ -283,8 +293,7 @@ async def _get_fields_json(self, name: str) -> List[Dict[str, Any]]:
283293 raise HasuraError (f'Unknown table `{ name } `' ) from e
284294
285295 async def _get_fields (self , name : str = 'query_root' ) -> List [Field ]:
286- # NOTE: Hasura omits default schema name
287- name = name .lstrip ('public_' )
296+ name = Field (name ).root
288297
289298 try :
290299 fields_json = await self ._get_fields_json (name )
@@ -320,17 +329,17 @@ async def _get_fields(self, name: str = 'query_root') -> List[Field]:
320329
321330 return fields
322331
323- async def _apply_camelcase (self ) -> None :
332+ async def _apply_table_customization (self ) -> None :
324333 """Convert table and column names to camelCase.
325334
326335 Based on https://github.com/m-rgba/hasura-snake-to-camel
327336 """
328- self ._logger .info ('Converting field names to camelCase' )
329337
330338 tables = await self ._get_fields ()
331339
340+ # TODO: Bulk request
332341 for table in tables :
333- custom_root_fields = self ._format_custom_root_fields (table . root )
342+ custom_root_fields = self ._format_custom_root_fields (table )
334343 columns = await self ._get_fields (table .root )
335344 custom_column_names = self ._format_custom_column_names (columns )
336345 args : Dict [str , Any ] = {
@@ -343,6 +352,7 @@ async def _apply_camelcase(self) -> None:
343352 },
344353 }
345354
355+ self ._logger .info ('Applying `%s` table customization' , table .root )
346356 await self ._hasura_request (
347357 endpoint = 'metadata' ,
348358 json = {
@@ -388,22 +398,32 @@ def _format_rest_endpoint(self, query_name: str) -> Dict[str, Any]:
388398 "comment" : None ,
389399 }
390400
391- def _format_custom_root_fields (self , table : str ) -> Dict [str , Any ]:
401+ def _format_custom_root_fields (self , table : Field ) -> Dict [str , Any ]:
402+ table_name = remove_prefix (table .root , self ._database_config .schema_name )
403+
404+ def _fmt (fmt : str ) -> str :
405+ if self ._hasura_config .camel_case :
406+ return humps .camelize (fmt .format (table_name ))
407+ return humps .decamelize (fmt .format (table_name ))
408+
392409 # NOTE: Do not change original Hasura format, REST endpoints generation will be broken otherwise
393410 return {
394- 'select' : humps . camelize ( table ),
395- 'select_by_pk' : humps . camelize ( f' { table } _by_pk' ),
396- 'select_aggregate' : humps . camelize ( f' { table } _aggregate' ),
397- 'insert' : humps . camelize ( f 'insert_{ table } ' ),
398- 'insert_one' : humps . camelize ( f 'insert_{ table } _one' ),
399- 'update' : humps . camelize ( f 'update_{ table } ' ),
400- 'update_by_pk' : humps . camelize ( f 'update_{ table } _by_pk' ),
401- 'delete' : humps . camelize ( f 'delete_{ table } ' ),
402- 'delete_by_pk' : humps . camelize ( f 'delete_{ table } _by_pk' ),
411+ 'select' : _fmt ( '{}' ),
412+ 'select_by_pk' : _fmt ( '{ }_by_pk' ),
413+ 'select_aggregate' : _fmt ( '{ }_aggregate' ),
414+ 'insert' : _fmt ( 'insert_{}' ),
415+ 'insert_one' : _fmt ( 'insert_{}_one' ),
416+ 'update' : _fmt ( 'update_{}' ),
417+ 'update_by_pk' : _fmt ( 'update_{}_by_pk' ),
418+ 'delete' : _fmt ( 'delete_{}' ),
419+ 'delete_by_pk' : _fmt ( 'delete_{}_by_pk' ),
403420 }
404421
405422 def _format_custom_column_names (self , fields : List [Field ]) -> Dict [str , Any ]:
406- return {humps .decamelize (f .name ): humps .camelize (f .name ) for f in fields }
423+ if self ._hasura_config .camel_case :
424+ return {humps .decamelize (f .name ): humps .camelize (f .name ) for f in fields }
425+ else :
426+ return {humps .decamelize (f .name ): humps .decamelize (f .name ) for f in fields }
407427
408428 def _format_table (self , name : str ) -> Dict [str , Any ]:
409429 return {
@@ -418,8 +438,7 @@ def _format_table(self, name: str) -> Dict[str, Any]:
418438 def _format_table_table (self , name : str ) -> Dict [str , Any ]:
419439 return {
420440 "schema" : self ._database_config .schema_name ,
421- # NOTE: Remove schema prefix from table name
422- 'name' : name .replace (self ._database_config .schema_name , '' ).strip ('_' ),
441+ 'name' : remove_prefix (name , self ._database_config .schema_name ),
423442 }
424443
425444 def _format_array_relationship (
0 commit comments