|
| 1 | +import importlib |
1 | 2 | import json |
2 | 3 | import logging |
3 | 4 | import os |
4 | 5 | import subprocess |
5 | 6 | from contextlib import suppress |
6 | 7 | from os import mkdir |
7 | 8 | from os.path import dirname, exists, join |
8 | | -from typing import Any, Dict |
| 9 | +from typing import List |
9 | 10 |
|
10 | 11 | from jinja2 import Template |
| 12 | +from tortoise import Model, fields |
11 | 13 |
|
12 | 14 | from dipdup.config import ROLLBACK_HANDLER, DipDupConfig |
13 | 15 | from dipdup.datasources.tzkt.datasource import TzktDatasource |
|
18 | 20 | async def create_package(config: DipDupConfig): |
19 | 21 | try: |
20 | 22 | package_path = config.package_path |
21 | | - except ImportError: |
| 23 | + except (ImportError, ModuleNotFoundError): |
22 | 24 | package_path = join(os.getcwd(), config.package) |
23 | 25 | mkdir(package_path) |
24 | 26 | with open(join(package_path, '__init__.py'), 'w'): |
@@ -147,3 +149,108 @@ async def generate_handlers(config: DipDupConfig): |
147 | 149 | if not exists(handler_path): |
148 | 150 | with open(handler_path, 'w') as file: |
149 | 151 | file.write(handler_code) |
| 152 | + |
| 153 | + |
| 154 | +def _format_array_relationship(related_name: str, table: str, column: str): |
| 155 | + return { |
| 156 | + "name": related_name, |
| 157 | + "using": { |
| 158 | + "foreign_key_constraint_on": { |
| 159 | + "column": column, |
| 160 | + "table": { |
| 161 | + "schema": "public", |
| 162 | + "name": table, |
| 163 | + }, |
| 164 | + }, |
| 165 | + }, |
| 166 | + } |
| 167 | + |
| 168 | + |
| 169 | +def _format_object_relationship(table: str, column: str): |
| 170 | + return { |
| 171 | + "name": table, |
| 172 | + "using": { |
| 173 | + "foreign_key_constraint_on": column, |
| 174 | + }, |
| 175 | + } |
| 176 | + |
| 177 | + |
| 178 | +def _format_select_permissions(columns: List[str]): |
| 179 | + return { |
| 180 | + "role": "user", |
| 181 | + "permission": { |
| 182 | + "columns": columns, |
| 183 | + "filter": {}, |
| 184 | + "allow_aggregations": True, |
| 185 | + }, |
| 186 | + } |
| 187 | + |
| 188 | + |
| 189 | +def _format_table(name: str): |
| 190 | + return { |
| 191 | + "table": { |
| 192 | + "schema": "public", |
| 193 | + "name": name, |
| 194 | + }, |
| 195 | + "object_relationships": [], |
| 196 | + "array_relationships": [], |
| 197 | + "select_permissions": [], |
| 198 | + } |
| 199 | + |
| 200 | + |
| 201 | +def _format_metadata(tables): |
| 202 | + return { |
| 203 | + "version": 2, |
| 204 | + "tables": tables, |
| 205 | + } |
| 206 | + |
| 207 | + |
| 208 | +async def generate_hasura_metadata(config: DipDupConfig): |
| 209 | + _logger.info('Generating Hasura metadata') |
| 210 | + metadata_tables = {} |
| 211 | + model_tables = {} |
| 212 | + models = importlib.import_module(f'{config.package}.models') |
| 213 | + |
| 214 | + for attr in dir(models): |
| 215 | + model = getattr(models, attr) |
| 216 | + if isinstance(model, type) and issubclass(model, Model) and model != Model: |
| 217 | + |
| 218 | + table_name = model._meta.db_table or model.__name__.lower() |
| 219 | + model_tables[f'models.{model.__name__}'] = table_name |
| 220 | + |
| 221 | + table = _format_table(table_name) |
| 222 | + metadata_tables[table_name] = table |
| 223 | + |
| 224 | + for attr in dir(models): |
| 225 | + model = getattr(models, attr) |
| 226 | + if isinstance(model, type) and issubclass(model, Model) and model != Model: |
| 227 | + table_name = model_tables[f'models.{model.__name__}'] |
| 228 | + |
| 229 | + metadata_tables[table_name]['select_permissions'].append( |
| 230 | + _format_select_permissions(list(model._meta.db_fields)), |
| 231 | + ) |
| 232 | + |
| 233 | + for field in model._meta.fields_map.values(): |
| 234 | + if isinstance(field, fields.relational.ForeignKeyFieldInstance): |
| 235 | + if not isinstance(field.related_name, str): |
| 236 | + raise Exception(f'`related_name` of `{field}` must be set') |
| 237 | + related_table_name = model_tables[field.model_name] |
| 238 | + metadata_tables[table_name]['object_relationships'].append( |
| 239 | + _format_object_relationship( |
| 240 | + table=model_tables[field.model_name], |
| 241 | + column=field.model_field_name + '_id', |
| 242 | + ) |
| 243 | + ) |
| 244 | + metadata_tables[related_table_name]['array_relationships'].append( |
| 245 | + _format_array_relationship( |
| 246 | + related_name=field.related_name, |
| 247 | + table=table_name, |
| 248 | + column=field.model_field_name + '_id', |
| 249 | + ) |
| 250 | + ) |
| 251 | + |
| 252 | + metadata = _format_metadata(tables=list(metadata_tables.values())) |
| 253 | + |
| 254 | + metadata_path = join(config.package_path, 'hasura_metadata.json') |
| 255 | + with open(metadata_path, 'w') as file: |
| 256 | + json.dump(metadata, file, indent=4) |
0 commit comments