tai-api 0.2.2__tar.gz → 0.2.4__tar.gz
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.
- {tai_api-0.2.2 → tai_api-0.2.4}/PKG-INFO +3 -3
- {tai_api-0.2.2 → tai_api-0.2.4}/pyproject.toml +3 -3
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_auth/main.py +19 -11
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_init/resources/__init__.py +1 -10
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_init/resources/handlers.py +2 -1
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/auth/keycloak.py +1 -1
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/auth/templates/kc/dependencies.py.j2 +6 -2
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/auth/templates/kc/keycloak_router.py.j2 +14 -47
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/auth/templates/kc/utils.py.j2 +0 -6
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/routers/templates/router_template.py.j2 +28 -2
- {tai_api-0.2.2 → tai_api-0.2.4}/LICENSE +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/README.md +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/__init__.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/__init__.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_auth/__init__.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_auth/database.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_auth/keycloak.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_dev/__init__.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_dev/main.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_generate/__init__.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_generate/funcs.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_generate/main.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_init/__init__.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_init/main.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_init/model.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_init/resources/exceptions.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_init/resources/mcp.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_init/resources/responses.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_mcp/__init__.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/commands/cmd_mcp/main.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/cli/main.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/config.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/__init__.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/auth/__init__.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/auth/db.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/auth/templates/db/__init__.py.j2 +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/auth/templates/db/auth_router.py.j2 +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/auth/templates/db/dependencies.py.j2 +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/auth/templates/db/jwt.py.j2 +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/auth/templates/kc/__init__.py.j2 +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/auth/templates/kc/login_router.py.j2 +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/main_file/__init__.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/main_file/main.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/main_file/templates/__dev__.py.j2 +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/main_file/templates/__main__.py.j2 +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/routers/__init__.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/routers/main.py +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/routers/templates/__init__.py.j2 +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/routers/templates/__outerinit__.py.j2 +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/routers/templates/enums_template.py.j2 +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/generators/routers/templates/macros.j2 +0 -0
- {tai_api-0.2.2 → tai_api-0.2.4}/tai_api/project.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tai-api
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary:
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: MateoSaezMata
|
|
@@ -20,8 +20,8 @@ Requires-Dist: jinja2 (>=3.1.0,<4.0.0)
|
|
|
20
20
|
Requires-Dist: pydantic (>=2.0.0,<3.0.0)
|
|
21
21
|
Requires-Dist: python-jose (>=3.5.0,<4.0.0)
|
|
22
22
|
Requires-Dist: tai-alphi (>=1.0.4,<2.0.0)
|
|
23
|
-
Requires-Dist: tai-keycloak (>=0.1.
|
|
24
|
-
Requires-Dist: tai-sql (>=0.3.
|
|
23
|
+
Requires-Dist: tai-keycloak (>=0.1.14,<0.2.0)
|
|
24
|
+
Requires-Dist: tai-sql (>=0.3.48,<0.4.0)
|
|
25
25
|
Requires-Dist: uvicorn[standard] (>=0.30.0,<0.31.0)
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "tai-api"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.4"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = ["MateoSaezMata <msaez@triplealpha.in>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -8,7 +8,7 @@ readme = "README.md"
|
|
|
8
8
|
[tool.poetry.dependencies]
|
|
9
9
|
python = "^3.10"
|
|
10
10
|
fastapi = {extras = ["standard"], version = "^0.116.1"}
|
|
11
|
-
tai-sql = "^0.3.
|
|
11
|
+
tai-sql = "^0.3.48"
|
|
12
12
|
jinja2 = "^3.1.0"
|
|
13
13
|
pydantic = "^2.0.0"
|
|
14
14
|
uvicorn = {extras = ["standard"], version = "^0.30.0"}
|
|
@@ -16,7 +16,7 @@ tai-alphi = "^1.0.4"
|
|
|
16
16
|
asyncpg = "^0.30.0"
|
|
17
17
|
python-jose = "^3.5.0"
|
|
18
18
|
fastapi-mcp = "^0.4.0"
|
|
19
|
-
tai-keycloak = "^0.1.
|
|
19
|
+
tai-keycloak = "^0.1.14"
|
|
20
20
|
click = "^8.3.0"
|
|
21
21
|
|
|
22
22
|
[tool.poetry.scripts]
|
|
@@ -48,18 +48,18 @@ def set_auth():
|
|
|
48
48
|
else:
|
|
49
49
|
click.echo("❌ Opción no válida. Por favor selecciona 1 o 2.")
|
|
50
50
|
|
|
51
|
-
# Verificar configuración de tai-sql
|
|
52
|
-
sqlconfig = sqlpm.get_project_config()
|
|
53
|
-
if not sqlconfig:
|
|
54
|
-
click.echo("❌ No se encontró la configuración de tai-sql.", err=True)
|
|
55
|
-
click.echo(" Asegúrate de haber inicializado el proyecto con tai-sql init.", err=True)
|
|
56
|
-
sys.exit(1)
|
|
57
|
-
|
|
58
|
-
# Establecer esquema por defecto si existe
|
|
59
|
-
if sqlpm.config.default_schema:
|
|
60
|
-
sqlpm.set_current_schema(sqlconfig.default_schema)
|
|
61
|
-
|
|
62
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
63
|
|
|
64
64
|
# Verificar que existe información de la base de datos
|
|
65
65
|
if not sqlpm.db or not sqlpm.db.tables:
|
|
@@ -135,6 +135,14 @@ def set_auth():
|
|
|
135
135
|
sys.exit(1)
|
|
136
136
|
|
|
137
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
|
+
|
|
138
146
|
for schema_name in sqlpm.discover_schemas():
|
|
139
147
|
sqlpm.set_current_schema(schema_name)
|
|
140
148
|
click.echo(f"Ejecutando: {click.style(RoutersGenerator.__name__, bold=True)}")
|
|
@@ -30,11 +30,6 @@ from .responses import (
|
|
|
30
30
|
PaginatedResponse
|
|
31
31
|
)
|
|
32
32
|
|
|
33
|
-
from .decorators import (
|
|
34
|
-
document_api_response,
|
|
35
|
-
document_paginated_response
|
|
36
|
-
)
|
|
37
|
-
|
|
38
33
|
from .handlers import setup_exception_handlers
|
|
39
34
|
|
|
40
35
|
from .mcp import set_mcp
|
|
@@ -46,11 +41,7 @@ __all__ = [
|
|
|
46
41
|
"PaginationMeta",
|
|
47
42
|
"APIResponse",
|
|
48
43
|
"PaginatedResponse",
|
|
49
|
-
|
|
50
|
-
# Decoradores de documentación
|
|
51
|
-
"document_api_response",
|
|
52
|
-
"document_paginated_response",
|
|
53
|
-
|
|
44
|
+
|
|
54
45
|
# Excepciones
|
|
55
46
|
"APIException",
|
|
56
47
|
"ErrorCode",
|
|
@@ -73,7 +73,8 @@ async def insufficient_permissions_handler(request: Request, exc: UnAuthorizedEx
|
|
|
73
73
|
|
|
74
74
|
api_error = APIError(
|
|
75
75
|
code=ErrorCode.INSUFFICIENT_PERMISSIONS,
|
|
76
|
-
message=
|
|
76
|
+
message=exc.message,
|
|
77
|
+
details=exc.details
|
|
77
78
|
)
|
|
78
79
|
|
|
79
80
|
response = APIResponse.error(api_error)
|
|
@@ -87,8 +87,8 @@ class AuthKeycloakGenerator:
|
|
|
87
87
|
context = {
|
|
88
88
|
'project_name': pm.config.name,
|
|
89
89
|
'resources_import_path': pm.config.resources_import_path,
|
|
90
|
-
'crud_import_path': pm.config.crud_import_path,
|
|
91
90
|
}
|
|
91
|
+
|
|
92
92
|
login_content = login_template.render(**context)
|
|
93
93
|
keycloak_content = keycloak_template.render(**context)
|
|
94
94
|
|
|
@@ -74,8 +74,12 @@ def requires_permissions(action: Literal['admin', 'read', 'write', 'update', 'de
|
|
|
74
74
|
token: Annotated[AccessToken, Depends(validate_token)]
|
|
75
75
|
) -> AccessToken:
|
|
76
76
|
|
|
77
|
-
#
|
|
78
|
-
|
|
77
|
+
# Resolver el permiso 'sudo'
|
|
78
|
+
if 'sudo' in token.api_roles:
|
|
79
|
+
return token
|
|
80
|
+
|
|
81
|
+
# Construir el permiso requerido en formato recurso-acción
|
|
82
|
+
required_perm = f"{resource}-{action}"
|
|
79
83
|
|
|
80
84
|
# Validar que el token tenga el permiso requerido
|
|
81
85
|
if required_perm not in token.api_roles:
|
|
@@ -4,14 +4,13 @@ Generado automáticamente por tai-api set-auth
|
|
|
4
4
|
|
|
5
5
|
Este módulo contiene endpoints para la gestión de usuarios en Keycloak.
|
|
6
6
|
"""
|
|
7
|
-
from tai_keycloak import User
|
|
7
|
+
from tai_keycloak import User, Group, RLSAttribute
|
|
8
8
|
from fastapi import APIRouter, Depends
|
|
9
9
|
from typing import List
|
|
10
10
|
from api.resources import APIResponse, PaginatedResponse
|
|
11
|
-
from {{ crud_import_path }} import *
|
|
12
11
|
|
|
13
12
|
from .dependencies import requires_permissions
|
|
14
|
-
from .utils import raise_if_exception, KeycloakDep
|
|
13
|
+
from .utils import raise_if_exception, KeycloakDep
|
|
15
14
|
|
|
16
15
|
kc_router = APIRouter(tags=["Keycloak"], dependencies=[Depends(requires_permissions('admin', 'keycloak'))])
|
|
17
16
|
|
|
@@ -56,7 +55,7 @@ async def list_users(
|
|
|
56
55
|
message=f"Usuarios obtenidos exitosamente"
|
|
57
56
|
)
|
|
58
57
|
|
|
59
|
-
@kc_router.get("/role", response_model=APIResponse[List[
|
|
58
|
+
@kc_router.get("/role", response_model=APIResponse[List[Group]])
|
|
60
59
|
async def list_roles(
|
|
61
60
|
kc: KeycloakDep
|
|
62
61
|
):
|
|
@@ -83,17 +82,17 @@ async def get_user(
|
|
|
83
82
|
message="Usuario obtenido exitosamente"
|
|
84
83
|
)
|
|
85
84
|
|
|
86
|
-
@kc_router.patch("/user", response_model=APIResponse[User])
|
|
85
|
+
@kc_router.patch("/user/{username}", response_model=APIResponse[User])
|
|
87
86
|
async def update_user(
|
|
87
|
+
username: str,
|
|
88
88
|
user: User,
|
|
89
89
|
kc: KeycloakDep
|
|
90
90
|
):
|
|
91
91
|
"""
|
|
92
|
-
Actualizar
|
|
93
|
-
NOTA: El objeto user debe incluir el username.
|
|
92
|
+
Actualizar atributos de un usuario en Keycloak
|
|
94
93
|
"""
|
|
95
94
|
|
|
96
|
-
result = raise_if_exception(await kc.user.update(user))
|
|
95
|
+
result = raise_if_exception(await kc.user.update(username, user))
|
|
97
96
|
|
|
98
97
|
return APIResponse.success(
|
|
99
98
|
data=result.data,
|
|
@@ -153,29 +152,23 @@ async def remove_role_from_user(
|
|
|
153
152
|
# ============ Endpoints de Atributos RLS (Row Level Security) ============
|
|
154
153
|
# Métodos disponibles: add_attribute, remove_attribute
|
|
155
154
|
|
|
156
|
-
@kc_router.
|
|
157
|
-
async def
|
|
155
|
+
@kc_router.put("/user/{username}/rls", response_model=APIResponse[bool])
|
|
156
|
+
async def set_rls(
|
|
158
157
|
username: str,
|
|
159
|
-
|
|
158
|
+
rls_attributes: List[RLSAttribute],
|
|
160
159
|
kc: KeycloakDep
|
|
161
160
|
):
|
|
162
161
|
"""Definir el RLS para un usuario para seguridad a nivel de fila"""
|
|
163
162
|
|
|
164
|
-
raise_if_exception(await kc.user.
|
|
163
|
+
raise_if_exception(await kc.user.set_attributes(username, rls_attributes))
|
|
165
164
|
|
|
166
165
|
return APIResponse.success(
|
|
167
166
|
data=True,
|
|
168
|
-
message="
|
|
169
|
-
meta={
|
|
170
|
-
"username": username,
|
|
171
|
-
"table": rls_attribute.table,
|
|
172
|
-
"field": rls_attribute.field,
|
|
173
|
-
"values": rls_attribute.values,
|
|
174
|
-
}
|
|
167
|
+
message="Atributos RLS agregado exitosamente"
|
|
175
168
|
)
|
|
176
169
|
|
|
177
170
|
@kc_router.get("/user/{username}/rls", response_model=APIResponse[List[RLSAttribute]])
|
|
178
|
-
async def
|
|
171
|
+
async def get_rls(
|
|
179
172
|
username: str,
|
|
180
173
|
kc: KeycloakDep
|
|
181
174
|
):
|
|
@@ -186,35 +179,9 @@ async def get_current_rls_conditions(
|
|
|
186
179
|
|
|
187
180
|
raise_if_exception(result)
|
|
188
181
|
|
|
189
|
-
current_rls = []
|
|
190
|
-
|
|
191
|
-
for rls, values in result.data.attributes.items():
|
|
192
|
-
_, table, field = rls.split('.', 2)
|
|
193
|
-
current_rls.append(RLSAttribute(table=table, field=field, values=values))
|
|
194
|
-
|
|
195
182
|
return APIResponse.success(
|
|
196
|
-
data=
|
|
183
|
+
data=result.data.attributes,
|
|
197
184
|
message="Atributos RLS obtenidos exitosamente",
|
|
198
185
|
meta={"username": username}
|
|
199
186
|
)
|
|
200
187
|
|
|
201
|
-
@kc_router.delete("/user/{username}/rls", response_model=APIResponse[bool])
|
|
202
|
-
async def delete_rls_condition(
|
|
203
|
-
username: str,
|
|
204
|
-
rls_attribute: RLSAttribute,
|
|
205
|
-
kc: KeycloakDep
|
|
206
|
-
):
|
|
207
|
-
"""Eliminar valores de atributos RLS de un usuario"""
|
|
208
|
-
|
|
209
|
-
raise_if_exception(await kc.user.remove_attribute(username, f'rls.{rls_attribute.table}.{rls_attribute.field}', rls_attribute.values))
|
|
210
|
-
|
|
211
|
-
return APIResponse.success(
|
|
212
|
-
data=True,
|
|
213
|
-
message="Atributo RLS eliminado exitosamente",
|
|
214
|
-
meta={
|
|
215
|
-
"username": username,
|
|
216
|
-
"table": rls_attribute.table,
|
|
217
|
-
"field": rls_attribute.field,
|
|
218
|
-
"values": rls_attribute.values,
|
|
219
|
-
}
|
|
220
|
-
)
|
|
@@ -12,12 +12,6 @@ from typing import List
|
|
|
12
12
|
from tai_keycloak.service.dtos import OperationResult
|
|
13
13
|
from api.resources import APIException, ErrorCode
|
|
14
14
|
|
|
15
|
-
class RLSAttribute(BaseModel):
|
|
16
|
-
table: str
|
|
17
|
-
field: str
|
|
18
|
-
values: str | List[str]
|
|
19
|
-
|
|
20
|
-
|
|
21
15
|
class LoginRequest(BaseModel):
|
|
22
16
|
"""Modelo para solicitud de login"""
|
|
23
17
|
username: str
|
|
@@ -151,13 +151,26 @@ async def {{ model.tablename }}_find_many(
|
|
|
151
151
|
{% endfor %}
|
|
152
152
|
{% endif %}
|
|
153
153
|
"""
|
|
154
|
+
{% if with_keycloak %}
|
|
155
|
+
rls = [
|
|
156
|
+
RLS(
|
|
157
|
+
target_model=sqlalchemy_table_mapper.get(table),
|
|
158
|
+
target_column=col_name,
|
|
159
|
+
values=values
|
|
160
|
+
) for table, conditions in (token.rls or {}).items()
|
|
161
|
+
for col_name, values in conditions.items()
|
|
162
|
+
]
|
|
163
|
+
{% endif %}
|
|
164
|
+
|
|
154
165
|
result = await api.{{ model.tablename }}.find_many(
|
|
155
166
|
limit=limit,
|
|
156
167
|
offset=offset,
|
|
157
168
|
order_by=order_by,
|
|
158
169
|
order=order,
|
|
159
170
|
{{ macros.asing_parameters(model).rstrip('\n') | indent(8) }}
|
|
160
|
-
includes=includes
|
|
171
|
+
includes=includes,
|
|
172
|
+
{% if with_keycloak %} rls=rls or None{% endif %}
|
|
173
|
+
|
|
161
174
|
)
|
|
162
175
|
|
|
163
176
|
# Obtener el total para metadatos de paginación si es necesario
|
|
@@ -338,12 +351,25 @@ async def {{ model.tablename }}_find(
|
|
|
338
351
|
raise ValidationException("{{ column.name }} debe ser mayor a 0", "{{ column.name }}")
|
|
339
352
|
{% endif %}
|
|
340
353
|
{% endfor %}
|
|
354
|
+
|
|
355
|
+
{% if with_keycloak %}
|
|
356
|
+
rls = [
|
|
357
|
+
RLS(
|
|
358
|
+
target_model=sqlalchemy_table_mapper.get(table),
|
|
359
|
+
target_column=col_name,
|
|
360
|
+
values=values
|
|
361
|
+
) for table, conditions in (token.rls or {}).items()
|
|
362
|
+
for col_name, values in conditions.items()
|
|
363
|
+
]
|
|
364
|
+
{% endif %}
|
|
341
365
|
|
|
342
366
|
result = await api.{{ model.tablename }}.find(
|
|
343
367
|
{% for column in model.columns if column.args.get('primary_key', False) %}
|
|
344
368
|
{{ column.name }}={{ column.name }},
|
|
345
369
|
{% endfor %}
|
|
346
|
-
includes=includes
|
|
370
|
+
includes=includes,
|
|
371
|
+
{% if with_keycloak %} rls=rls or None{% endif %}
|
|
372
|
+
|
|
347
373
|
)
|
|
348
374
|
|
|
349
375
|
if result is None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|