tai-api 0.2.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. tai_api/__init__.py +19 -0
  2. tai_api/cli/commands/__init__.py +13 -0
  3. tai_api/cli/commands/cmd_auth/__init__.py +1 -0
  4. tai_api/cli/commands/cmd_auth/database.py +153 -0
  5. tai_api/cli/commands/cmd_auth/keycloak.py +24 -0
  6. tai_api/cli/commands/cmd_auth/main.py +179 -0
  7. tai_api/cli/commands/cmd_dev/__init__.py +1 -0
  8. tai_api/cli/commands/cmd_dev/main.py +27 -0
  9. tai_api/cli/commands/cmd_generate/__init__.py +5 -0
  10. tai_api/cli/commands/cmd_generate/funcs.py +61 -0
  11. tai_api/cli/commands/cmd_generate/main.py +44 -0
  12. tai_api/cli/commands/cmd_init/__init__.py +1 -0
  13. tai_api/cli/commands/cmd_init/main.py +22 -0
  14. tai_api/cli/commands/cmd_init/model.py +160 -0
  15. tai_api/cli/commands/cmd_init/resources/__init__.py +67 -0
  16. tai_api/cli/commands/cmd_init/resources/exceptions.py +232 -0
  17. tai_api/cli/commands/cmd_init/resources/handlers.py +334 -0
  18. tai_api/cli/commands/cmd_init/resources/mcp.py +76 -0
  19. tai_api/cli/commands/cmd_init/resources/responses.py +180 -0
  20. tai_api/cli/commands/cmd_mcp/__init__.py +5 -0
  21. tai_api/cli/commands/cmd_mcp/main.py +37 -0
  22. tai_api/cli/main.py +22 -0
  23. tai_api/config.py +227 -0
  24. tai_api/generators/__init__.py +10 -0
  25. tai_api/generators/auth/__init__.py +2 -0
  26. tai_api/generators/auth/db.py +185 -0
  27. tai_api/generators/auth/keycloak.py +151 -0
  28. tai_api/generators/auth/templates/db/__init__.py.j2 +28 -0
  29. tai_api/generators/auth/templates/db/auth_router.py.j2 +230 -0
  30. tai_api/generators/auth/templates/db/dependencies.py.j2 +69 -0
  31. tai_api/generators/auth/templates/db/jwt.py.j2 +103 -0
  32. tai_api/generators/auth/templates/kc/__init__.py.j2 +28 -0
  33. tai_api/generators/auth/templates/kc/dependencies.py.j2 +96 -0
  34. tai_api/generators/auth/templates/kc/keycloak_router.py.j2 +187 -0
  35. tai_api/generators/auth/templates/kc/login_router.py.j2 +135 -0
  36. tai_api/generators/auth/templates/kc/utils.py.j2 +57 -0
  37. tai_api/generators/main_file/__init__.py +14 -0
  38. tai_api/generators/main_file/main.py +86 -0
  39. tai_api/generators/main_file/templates/__dev__.py.j2 +74 -0
  40. tai_api/generators/main_file/templates/__main__.py.j2 +114 -0
  41. tai_api/generators/routers/__init__.py +3 -0
  42. tai_api/generators/routers/main.py +196 -0
  43. tai_api/generators/routers/templates/__init__.py.j2 +7 -0
  44. tai_api/generators/routers/templates/__outerinit__.py.j2 +7 -0
  45. tai_api/generators/routers/templates/enums_template.py.j2 +26 -0
  46. tai_api/generators/routers/templates/macros.j2 +324 -0
  47. tai_api/generators/routers/templates/router_template.py.j2 +1003 -0
  48. tai_api/project.py +177 -0
  49. tai_api-0.2.5.dist-info/METADATA +28 -0
  50. tai_api-0.2.5.dist-info/RECORD +53 -0
  51. tai_api-0.2.5.dist-info/WHEEL +4 -0
  52. tai_api-0.2.5.dist-info/entry_points.txt +3 -0
  53. tai_api-0.2.5.dist-info/licenses/LICENSE +674 -0
tai_api/__init__.py ADDED
@@ -0,0 +1,19 @@
1
+ from .project import ProjectManager as pm
2
+ from .config import (
3
+ ProjectConfig,
4
+ AuthConfig,
5
+ AuthType,
6
+ DatabaseAuthConfig,
7
+ KeycloakAuthConfig
8
+ )
9
+ from .generators import MainFileGenerator
10
+
11
+ __all__ = [
12
+ 'ProjectConfig',
13
+ 'AuthConfig',
14
+ 'AuthType',
15
+ 'DatabaseAuthConfig',
16
+ 'KeycloakAuthConfig',
17
+ 'MainFileGenerator',
18
+ 'pm'
19
+ ]
@@ -0,0 +1,13 @@
1
+ from .cmd_generate import generate
2
+ from .cmd_init import init
3
+ from .cmd_dev import dev
4
+ from .cmd_auth import set_auth
5
+ from .cmd_mcp import set_mcp
6
+
7
+ __all__ = [
8
+ "generate",
9
+ "init",
10
+ "dev",
11
+ "set_auth",
12
+ "set_mcp",
13
+ ]
@@ -0,0 +1 @@
1
+ from .main import set_auth
@@ -0,0 +1,153 @@
1
+ import sys
2
+ import click
3
+
4
+ from tai_sql import pm as sqlpm
5
+ from tai_api import DatabaseAuthConfig
6
+
7
+ def rundbconfig() -> DatabaseAuthConfig:
8
+ """
9
+ Configura la autenticación basada en base de datos.
10
+ """
11
+ click.echo("\n🗄️ Configurando autenticación de base de datos...")
12
+ click.echo("-" * 40)
13
+
14
+ if not sqlpm.db.tables:
15
+ click.echo("❌ No se encontraron tablas en la base de datos.", err=True)
16
+ sys.exit(1)
17
+
18
+ tables = sqlpm.db.tables
19
+
20
+ # Mostrar tablas disponibles
21
+ click.echo("\n📊 Tablas disponibles:")
22
+ for i, table in enumerate(tables, 1):
23
+ column_count = len(table.columns)
24
+ click.echo(f" {i}. {table._name} ({column_count} columnas)")
25
+
26
+ # Seleccionar tabla de usuarios
27
+ while True:
28
+ choice: int = click.prompt(
29
+ f"\n🔢 Selecciona la tabla de usuarios (1-{len(tables)})",
30
+ type=int,
31
+ show_default=False
32
+ )
33
+
34
+ if 1 <= choice <= len(tables):
35
+ selected_table = tables[choice - 1]
36
+ break
37
+ else:
38
+ click.echo(f"❌ Opción no válida. Selecciona un número entre 1 y {len(tables)}.")
39
+
40
+ click.echo(f"✅ Tabla seleccionada: {selected_table._name}")
41
+
42
+ columns = list(selected_table.columns.values())
43
+
44
+ # Seleccionar campo de username
45
+ click.echo(f"\n🏷️ Selecciona el campo para 'username':")
46
+ for i, column in enumerate(columns, 1):
47
+ click.echo(f" {i}. {column.name} ({column.type})")
48
+
49
+ while True:
50
+ choice: int = click.prompt(
51
+ f"🔢 Selecciona el campo de 'username' (1-{len(columns)})",
52
+ type=int,
53
+ show_default=False
54
+ )
55
+
56
+ if 1 <= choice <= len(columns):
57
+ username_field = columns[choice - 1]
58
+ if not username_field.args.primary_key:
59
+ click.echo("❌ El campo de 'username' debe ser una clave primaria.", err=True)
60
+ continue
61
+ break
62
+ else:
63
+ click.echo(f"❌ Opción no válida. Selecciona un número entre 1 y {len(columns)}.")
64
+
65
+ click.echo(f"✅ Campo de username: {username_field.name}")
66
+
67
+ # Seleccionar campo de password
68
+ click.echo(f"\n🏷️ Selecciona el campo para 'password':")
69
+ for i, column in enumerate(columns, 1):
70
+ click.echo(f" {i}. {column.name} ({column.type})")
71
+
72
+ while True:
73
+ choice: int = click.prompt(
74
+ f"🔢 Selecciona el campo de 'password' (1-{len(columns)})",
75
+ type=int,
76
+ show_default=False
77
+ )
78
+
79
+ if 1 <= choice <= len(columns):
80
+ password_field = columns[choice - 1]
81
+ break
82
+ else:
83
+ click.echo(f"❌ Opción no válida. Selecciona un número entre 1 y {len(columns)}.")
84
+
85
+ click.echo(f"✅ Campo de password: {password_field.name}")
86
+
87
+ # Seleccionar campo de session_id (opcional)
88
+ click.echo(f"\n🔐 ¿Deseas configurar manejo de sesiones concurrentes?")
89
+ click.echo(" Esto permite invalidar sesiones cuando un usuario se loguea desde otro lugar.")
90
+ click.echo(" Si seleccionas 'Sí', debes elegir un campo para almacenar el session_id.")
91
+
92
+ session_management = click.confirm("\n🔄 ¿Habilitar manejo de sesiones?", default=True)
93
+ session_id_field = None
94
+
95
+ if session_management:
96
+ click.echo(f"\n🏷️ Selecciona el campo para 'session_id':")
97
+ click.echo(" (Este campo debe poder almacenar texto, como VARCHAR, TEXT, etc.)")
98
+ for i, column in enumerate(columns, 1):
99
+ click.echo(f" {i}. {column.name} ({column.type})")
100
+
101
+ while True:
102
+ choice: int = click.prompt(
103
+ f"🔢 Selecciona el campo de 'session_id' (1-{len(columns)})",
104
+ type=int,
105
+ show_default=False
106
+ )
107
+
108
+ if 1 <= choice <= len(columns):
109
+ session_id_field = columns[choice - 1]
110
+ break
111
+ else:
112
+ click.echo(f"❌ Opción no válida. Selecciona un número entre 1 y {len(columns)}.")
113
+
114
+ click.echo(f"✅ Campo de session_id: {session_id_field.name}")
115
+
116
+ # Seleccionar campo de password_expiration (opcional)
117
+ click.echo(f"\n🔐 ¿Deseas configurar renovación de contraseñas?")
118
+ click.echo(" Esto obliga al usuario a renovar su contraseña cada cierto tiempo.")
119
+
120
+ pwd_renewal = click.confirm("\n🔄 ¿Habilitar renovación de contraseñas?", default=True)
121
+ pwd_expiration_field = None
122
+
123
+ if pwd_renewal:
124
+ click.echo(f"\n🏷️ Selecciona el campo para 'password_expiration':")
125
+ click.echo(" (Este campo debe ser DATE o DATETIME.)")
126
+ for i, column in enumerate(columns, 1):
127
+ click.echo(f" {i}. {column.name} ({column.type})")
128
+
129
+ while True:
130
+ choice: int = click.prompt(
131
+ f"🔢 Selecciona el campo de 'session_id' (1-{len(columns)})",
132
+ type=int,
133
+ show_default=False
134
+ )
135
+
136
+ if 1 <= choice <= len(columns):
137
+ pwd_expiration_field = columns[choice - 1]
138
+ break
139
+ else:
140
+ click.echo(f"❌ Opción no válida. Selecciona un número entre 1 y {len(columns)}.")
141
+
142
+ click.echo(f"✅ Campo de password_expiration: {pwd_expiration_field.name}")
143
+ click.echo("💡 El sistema generará un UUID único para cada sesión y lo almacenará en este campo.")
144
+ else:
145
+ click.echo("✅ Manejo de sesiones deshabilitado (se permitirán múltiples sesiones concurrentes)")
146
+
147
+ return DatabaseAuthConfig(
148
+ table_name=selected_table._name,
149
+ username_field=username_field.name,
150
+ password_field=password_field.name,
151
+ session_id_field=session_id_field.name,
152
+ password_expiration_field=pwd_expiration_field.name if pwd_renewal else None,
153
+ )
@@ -0,0 +1,24 @@
1
+ import click
2
+ from tai_api import KeycloakAuthConfig
3
+
4
+ def runkeycloakconfig() -> KeycloakAuthConfig:
5
+ click.echo(click.style("🚀 Running Keycloak configuration...", fg='cyan', bold=True))
6
+ click.echo("")
7
+ click.echo(click.style("📋 Para que la API funcione correctamente con Keycloak, necesitas provisionar las siguientes variables de entorno:", fg='yellow', bold=True))
8
+ click.echo("")
9
+ click.echo(click.style("1️⃣ MAIN_KEYCLOAK_URL", fg='blue', bold=True))
10
+ click.echo(click.style(" Formato: ", fg='white') + click.style("MAIN_KEYCLOAK_URL=<protocol>://<user>:<pwd>@<host>:<port>", fg='magenta'))
11
+ click.echo(click.style(" Ejemplo: ", fg='white') + click.style("MAIN_KEYCLOAK_URL=http://admin:admin@localhost:8090", fg='green'))
12
+ click.echo("")
13
+ click.echo(click.style("2️⃣ KEYCLOAK_API_CLIENT_SECRET", fg='blue', bold=True))
14
+ click.echo(click.style(" Formato: ", fg='white') + click.style("KEYCLOAK_API_CLIENT_SECRET='secreto_del_cliente_api'", fg='magenta'))
15
+ click.echo("")
16
+ click.echo(click.style("🔍 Para encontrar el KEYCLOAK_API_CLIENT_SECRET:", fg='yellow', bold=True))
17
+ click.echo(click.style(" 1. Accede a ", fg='white') + click.style("<host>:<port>", fg='cyan') + click.style(" (ej: ", fg='white') + click.style("localhost:8090", fg='green') + click.style(")", fg='white'))
18
+ click.echo(click.style(" 2. Ve a ", fg='white') + click.style("Main Realm > Clients > \"api\" > Credentials", fg='cyan'))
19
+ click.echo(click.style(" 3. Copia el Client Secret que aparece ahí", fg='white'))
20
+ click.echo("")
21
+ click.echo(click.style("✅ Una vez configuradas estas variables, la API podrá autenticarse con Keycloak", fg='green', bold=True))
22
+ click.echo("")
23
+
24
+ return KeycloakAuthConfig(realm_name="main-realm", client_name="api", audience="api")
@@ -0,0 +1,179 @@
1
+ import sys
2
+ import click
3
+
4
+ from tai_sql import pm as sqlpm
5
+ from tai_sql.generators import BaseGenerator
6
+ from tai_api import pm, AuthConfig, AuthType
7
+ from tai_api.generators import AuthDatabaseGenerator, AuthKeycloakGenerator, MainFileGenerator, RoutersGenerator
8
+
9
+ from .database import rundbconfig
10
+ from .keycloak import runkeycloakconfig
11
+
12
+ @click.command()
13
+ def set_auth():
14
+ """Genera recursos para la seguridad de la API"""
15
+
16
+ click.echo("🔐 Configuración de Autenticación - tai-api")
17
+ click.echo("=" * 50)
18
+
19
+ # Verificar configuración de tai-api
20
+ config = pm.get_project_config()
21
+ if not config:
22
+ click.echo("❌ No se encontró la configuración del proyecto tai-api.", err=True)
23
+ click.echo(" Asegúrate de haber inicializado el proyecto con tai-api init.", err=True)
24
+ sys.exit(1)
25
+
26
+ # Seleccionar tipo de autenticación
27
+ click.echo("\n📋 Selecciona el tipo de autenticación:")
28
+ click.echo(" 1. Database - Autenticación basada en base de datos")
29
+ click.echo(" 2. Keycloak - Autenticación con Keycloak")
30
+
31
+ while True:
32
+ choice = click.prompt(
33
+ "\n🔢 Selecciona una opción (1 o 2)",
34
+ type=int,
35
+ show_default=False
36
+ )
37
+
38
+ if choice == 1:
39
+ click.echo("✅ Has seleccionado: Database")
40
+ click.echo()
41
+ auth_type = "database"
42
+ break
43
+ elif choice == 2:
44
+ click.echo("✅ Has seleccionado: Keycloak")
45
+ click.echo()
46
+ auth_type = "keycloak"
47
+ break
48
+ else:
49
+ click.echo("❌ Opción no válida. Por favor selecciona 1 o 2.")
50
+
51
+ if auth_type == "database":
52
+
53
+ # Verificar configuración de tai-sql
54
+ sqlconfig = sqlpm.get_project_config()
55
+ if not sqlconfig:
56
+ click.echo("❌ No se encontró la configuración de tai-sql.", err=True)
57
+ click.echo(" Asegúrate de haber inicializado el proyecto con tai-sql init.", err=True)
58
+ sys.exit(1)
59
+
60
+ # Establecer esquema por defecto si existe
61
+ if sqlpm.config.default_schema:
62
+ sqlpm.set_current_schema(sqlconfig.default_schema)
63
+
64
+ # Verificar que existe información de la base de datos
65
+ if not sqlpm.db or not sqlpm.db.tables:
66
+ click.echo("❌ No se encontró información de tablas en la base de datos.", err=True)
67
+ sys.exit(1)
68
+
69
+ # Obtener configuración de la base de datos
70
+ db_auth_config = rundbconfig()
71
+
72
+ # Crear configuración de autenticación
73
+ auth_config = AuthConfig(
74
+ type=AuthType.DATABASE,
75
+ config=db_auth_config
76
+ )
77
+
78
+ # Guardar en la configuración del proyecto
79
+ try:
80
+ pm.update_auth_config(auth_config)
81
+ except ValueError as e:
82
+ click.echo(f"❌ Error al guardar la configuración: {e}", err=True)
83
+ sys.exit(1)
84
+
85
+ # Mostrar mensaje de configuración
86
+ click.echo("\n⚙️ Configuración de autenticación...")
87
+ click.echo("-" * 40)
88
+ click.echo(f"📝 Configuración seleccionada: database")
89
+ click.echo(f" • Tabla: {db_auth_config.table_name}")
90
+ click.echo(f" • Campo username: {db_auth_config.username_field}")
91
+ click.echo(f" • Campo password: {db_auth_config.password_field}")
92
+
93
+ if db_auth_config.has_session_management:
94
+ click.echo(f" • Campo session_id: {db_auth_config.session_id_field}")
95
+ click.echo(" • ✅ Manejo de sesiones concurrentes habilitado")
96
+ else:
97
+ click.echo(" • ❌ Manejo de sesiones concurrentes deshabilitado")
98
+ click.echo("")
99
+
100
+ auth_generator = AuthDatabaseGenerator(output_dir=pm.config.auth_namespace.as_posix())
101
+ main_file_generator = MainFileGenerator(
102
+ output_dir=pm.config.main_namespace.as_posix()
103
+ )
104
+
105
+ generators: list[BaseGenerator] = [auth_generator, main_file_generator]
106
+
107
+ for generator in generators:
108
+
109
+ generator_name = generator.__class__.__name__
110
+ click.echo(f"Ejecutando: {click.style(generator_name, bold=True)}")
111
+
112
+ # El generador se encargará de descubrir los modelos internamente
113
+ result = generator.generate()
114
+
115
+ click.echo(f"✅ Generador {generator_name} completado con éxito.")
116
+ if result:
117
+ click.echo(f" Recursos en: {result}")
118
+ click.echo("")
119
+
120
+ elif auth_type == "keycloak":
121
+ # Obtener configuración de Keycloak
122
+ kc_auth_config = runkeycloakconfig()
123
+
124
+ # Crear configuración de autenticación
125
+ auth_config = AuthConfig(
126
+ type=AuthType.KEYCLOAK,
127
+ config=kc_auth_config
128
+ )
129
+
130
+ # Guardar en la configuración del proyecto
131
+ try:
132
+ pm.update_auth_config(auth_config)
133
+ except ValueError as e:
134
+ click.echo(f"❌ Error al guardar la configuración: {e}", err=True)
135
+ sys.exit(1)
136
+
137
+ if pm.config.has_routers:
138
+
139
+ # Verificar configuración de tai-sql
140
+ sqlconfig = sqlpm.get_project_config()
141
+ if not sqlconfig:
142
+ click.echo("❌ No se encontró la configuración de tai-sql.", err=True)
143
+ click.echo(" Asegúrate de haber inicializado el proyecto con tai-sql init.", err=True)
144
+ sys.exit(1)
145
+
146
+ for schema_name in sqlpm.discover_schemas():
147
+ sqlpm.set_current_schema(schema_name)
148
+ click.echo(f"Ejecutando: {click.style(RoutersGenerator.__name__, bold=True)}")
149
+ click.echo(f" • Esquema: {schema_name}")
150
+ result = RoutersGenerator(
151
+ output_dir=(pm.config.routers_namespace / schema_name).as_posix()
152
+ ).generate()
153
+ click.echo(f"✅ Generador {RoutersGenerator.__name__} completado con éxito.")
154
+ if result:
155
+ click.echo(f" Recursos en: {result}")
156
+ click.echo("")
157
+
158
+ auth_generator = AuthKeycloakGenerator(output_dir=pm.config.auth_namespace.as_posix())
159
+ main_file_generator = MainFileGenerator(
160
+ output_dir=pm.config.main_namespace.as_posix()
161
+ )
162
+
163
+ base_generators: list[BaseGenerator] = [auth_generator, main_file_generator]
164
+
165
+ for generator in base_generators:
166
+
167
+ generator_name = generator.__class__.__name__
168
+ click.echo(f"Ejecutando: {click.style(generator_name, bold=True)}")
169
+
170
+ # El generador se encargará de descubrir los modelos internamente
171
+ result = generator.generate()
172
+
173
+ click.echo(f"✅ Generador {generator_name} completado con éxito.")
174
+ if result:
175
+ click.echo(f" Recursos en: {result}")
176
+ click.echo("")
177
+ else:
178
+ click.echo("❌ Opción no válida.", err=True)
179
+ sys.exit(1)
@@ -0,0 +1 @@
1
+ from .main import dev
@@ -0,0 +1,27 @@
1
+ import sys
2
+ import click
3
+ import subprocess
4
+
5
+ from tai_api import pm
6
+
7
+ @click.command()
8
+ @click.option('--auth', '-w', is_flag=True, help='Activar autenticación')
9
+ def dev(auth: bool = False):
10
+ """Inicia el servidor en modo desarrollo."""
11
+
12
+ config = pm.get_project_config()
13
+
14
+ if not config:
15
+ click.echo("❌ No se encontró la configuración del proyecto. Asegúrate de haber inicializado el proyecto con tai-api init.", err=True)
16
+ sys.exit(1)
17
+
18
+ if auth:
19
+ if pm.config.auth is None:
20
+ click.echo("⚠️ Advertencia: Se ejecutará sin autenticación porque no ha sido configurada.", err=True)
21
+ file_name = '__main__.py'
22
+ else:
23
+ file_name = '__dev__.py'
24
+
25
+ main_file = pm.config.main_namespace / file_name
26
+
27
+ sys.exit(subprocess.call(["fastapi", "dev", main_file.as_posix()]))
@@ -0,0 +1,5 @@
1
+ from .main import generate
2
+
3
+ __all__ = [
4
+ "generate",
5
+ ]
@@ -0,0 +1,61 @@
1
+ import click
2
+ import sys
3
+ from tai_sql.generators import (
4
+ BaseGenerator,
5
+ ModelsGenerator,
6
+ CRUDGenerator,
7
+ ERDiagramGenerator
8
+ )
9
+ from tai_api.generators import RoutersGenerator, MainFileGenerator
10
+
11
+ from tai_api import pm
12
+ from tai_sql import pm as sqlpm
13
+
14
+
15
+ def run_generate():
16
+ """Run the configured generators."""
17
+ # Ejecutar cada generador
18
+ click.echo("🚀 Ejecutando generadores...")
19
+ click.echo()
20
+
21
+ models_generator = ModelsGenerator(pm.config.database_namespace.as_posix())
22
+ crud_generator = CRUDGenerator(
23
+ output_dir=pm.config.database_namespace.as_posix(),
24
+ models_import_path=pm.config.models_import_path,
25
+ mode='async'
26
+ )
27
+ er_generator = ERDiagramGenerator(pm.config.diagrams_namespace.as_posix())
28
+
29
+ endpoints_generator = RoutersGenerator(
30
+ output_dir=(pm.config.routers_namespace / sqlpm.db.schema_name).as_posix()
31
+ )
32
+
33
+ main_file_generator = MainFileGenerator(
34
+ output_dir=pm.config.main_namespace.as_posix()
35
+ )
36
+
37
+ generators: list[BaseGenerator] = [
38
+ models_generator,
39
+ crud_generator,
40
+ er_generator,
41
+ endpoints_generator,
42
+ main_file_generator
43
+ ]
44
+
45
+ for generator in generators:
46
+ try:
47
+ generator_name = generator.__class__.__name__
48
+ click.echo(f"Ejecutando: {click.style(generator_name, bold=True)}")
49
+
50
+ # El generador se encargará de descubrir los modelos internamente
51
+ result = generator.generate()
52
+
53
+ click.echo(f"✅ Generador {generator_name} completado con éxito.")
54
+ if result:
55
+ click.echo(f" Recursos en: {result}")
56
+ except Exception as e:
57
+ click.echo(f"❌ Error al ejecutar el generador {generator_name}: {str(e)}", err=True)
58
+ sys.exit(1)
59
+
60
+ finally:
61
+ click.echo()
@@ -0,0 +1,44 @@
1
+ import sys
2
+ import click
3
+
4
+ from tai_sql import pm as sqlpm
5
+ from tai_api import pm
6
+ from .funcs import run_generate
7
+
8
+ @click.command()
9
+ @click.option('--schema', '-s', help='Nombre del esquema')
10
+ @click.option('--all', is_flag=True, help='Generar para todos los esquemas')
11
+ def generate(schema: str=None, all: bool=False):
12
+ """Genera recursos para la API."""
13
+
14
+ if not pm.get_project_config():
15
+ click.echo("❌ No se encontró la configuración del proyecto. Asegúrate de haber inicializado el proyecto con tai-api init.", err=True)
16
+ sys.exit(1)
17
+
18
+ if schema and all:
19
+ click.echo("❌ Las opciones --schema y --all no pueden usarse juntas.", err=True)
20
+ sys.exit(1)
21
+
22
+ if schema:
23
+ sqlpm.set_current_schema(schema)
24
+ run_generate()
25
+
26
+ elif all:
27
+ for schema_name in sqlpm.discover_schemas():
28
+ click.echo(f"\n🔄 Generando para esquema: {schema_name}\n")
29
+ sqlpm.set_current_schema(schema_name)
30
+ run_generate()
31
+
32
+ else:
33
+ sqlconfig = sqlpm.get_project_config()
34
+ if sqlconfig:
35
+ sqlpm.set_current_schema(sqlconfig.default_schema)
36
+
37
+
38
+ if not schema and not sqlpm.db:
39
+ click.echo(f"❌ No existe ningún esquema por defecto", err=True)
40
+ click.echo(f" Puedes definir uno con: tai-sql set-default-schema <nombre>", err=True)
41
+ click.echo(f" O usar la opción: --schema <nombre_esquema>", err=True)
42
+ sys.exit(1)
43
+
44
+ run_generate()
@@ -0,0 +1 @@
1
+ from .main import init
@@ -0,0 +1,22 @@
1
+ import sys
2
+ import click
3
+ from .model import InitCommand
4
+
5
+ @click.command()
6
+ @click.argument('project', type=str)
7
+ @click.option('--namespace', '-n', default='api', help='Nombre del proyecto a crear')
8
+ def init(project: str, namespace: str):
9
+ """Inicializa un nuevo proyecto tai-api"""
10
+ command = InitCommand(project=project, namespace=namespace)
11
+ try:
12
+ command.check_poetry()
13
+ command.check_directory_is_avaliable()
14
+ command.check_virtualenv()
15
+ command.create_project()
16
+ command.create_project_config()
17
+ command.add_dependencies()
18
+ command.add_folders()
19
+ command.msg()
20
+ except Exception as e:
21
+ click.echo(f"❌ Error al inicializar el proyecto: {str(e)}", err=True)
22
+ sys.exit(1)