nlbone 0.11.0__tar.gz → 0.11.1__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.
Files changed (126) hide show
  1. {nlbone-0.11.0 → nlbone-0.11.1}/PKG-INFO +1 -1
  2. {nlbone-0.11.0 → nlbone-0.11.1}/pyproject.toml +1 -1
  3. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/db/postgres/query_builder.py +52 -1
  4. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/additional_filed/assembler.py +50 -0
  5. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/additional_filed/field_registry.py +2 -1
  6. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/additional_filed/resolver.py +79 -0
  7. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/dependencies/async_auth.py +1 -1
  8. {nlbone-0.11.0 → nlbone-0.11.1}/.gitignore +0 -0
  9. {nlbone-0.11.0 → nlbone-0.11.1}/LICENSE +0 -0
  10. {nlbone-0.11.0 → nlbone-0.11.1}/README.md +0 -0
  11. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/__init__.py +0 -0
  12. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/__init__.py +0 -0
  13. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/auth/__init__.py +0 -0
  14. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/auth/async_auth_service.py +0 -0
  15. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/auth/async_token_provider.py +0 -0
  16. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/auth/auth_service.py +0 -0
  17. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/auth/keycloak.py +0 -0
  18. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/auth/token_provider.py +0 -0
  19. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/cache/__init__.py +0 -0
  20. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/cache/async_redis.py +0 -0
  21. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/cache/memory.py +0 -0
  22. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/cache/pubsub_listener.py +0 -0
  23. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/cache/redis.py +0 -0
  24. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/db/__init__.py +0 -0
  25. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/db/postgres/__init__.py +0 -0
  26. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/db/postgres/audit.py +0 -0
  27. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/db/postgres/base.py +0 -0
  28. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/db/postgres/engine.py +0 -0
  29. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/db/postgres/repository.py +0 -0
  30. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/db/postgres/schema.py +0 -0
  31. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/db/postgres/types.py +0 -0
  32. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/db/postgres/uow.py +0 -0
  33. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/db/redis/__init__.py +0 -0
  34. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/db/redis/client.py +0 -0
  35. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/http_clients/__init__.py +0 -0
  36. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/http_clients/pricing/__init__.py +0 -0
  37. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/http_clients/pricing/async_pricing_service.py +0 -0
  38. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/http_clients/pricing/pricing_service.py +0 -0
  39. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/http_clients/uploadchi/__init__.py +0 -0
  40. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/http_clients/uploadchi/uploadchi.py +0 -0
  41. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/http_clients/uploadchi/uploadchi_async.py +0 -0
  42. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/i18n/__init__.py +0 -0
  43. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/i18n/engine.py +0 -0
  44. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/i18n/loaders.py +0 -0
  45. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/i18n/locales/fa-IR.json +0 -0
  46. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/messaging/__init__.py +0 -0
  47. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/messaging/event_bus.py +0 -0
  48. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/messaging/rabbitmq.py +0 -0
  49. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/outbox/__init__.py +0 -0
  50. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/outbox/outbox_consumer.py +0 -0
  51. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/outbox/outbox_repo.py +0 -0
  52. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/percolation/__init__.py +0 -0
  53. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/percolation/connection.py +0 -0
  54. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/repositories/__init__.py +0 -0
  55. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/snowflake.py +0 -0
  56. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/ticketing/__init__.py +0 -0
  57. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/adapters/ticketing/client.py +0 -0
  58. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/config/__init__.py +0 -0
  59. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/config/logging.py +0 -0
  60. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/config/settings.py +0 -0
  61. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/container.py +0 -0
  62. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/__init__.py +0 -0
  63. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/application/__init__.py +0 -0
  64. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/application/base_worker.py +0 -0
  65. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/application/bus.py +0 -0
  66. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/application/di.py +0 -0
  67. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/application/registry.py +0 -0
  68. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/application/services/__init__.py +0 -0
  69. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/application/use_case.py +0 -0
  70. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/domain/__init__.py +0 -0
  71. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/domain/base.py +0 -0
  72. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/domain/models.py +0 -0
  73. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/ports/__init__.py +0 -0
  74. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/ports/auth.py +0 -0
  75. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/ports/cache.py +0 -0
  76. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/ports/event_bus.py +0 -0
  77. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/ports/files.py +0 -0
  78. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/ports/outbox.py +0 -0
  79. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/ports/repository.py +0 -0
  80. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/ports/translation.py +0 -0
  81. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/core/ports/uow.py +0 -0
  82. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/__init__.py +0 -0
  83. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/__init__.py +0 -0
  84. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/additional_filed/__init__.py +0 -0
  85. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/additional_filed/default_field_rules/__init__.py +0 -0
  86. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/additional_filed/default_field_rules/image_field_rules.py +0 -0
  87. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/dependencies/__init__.py +0 -0
  88. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/dependencies/auth.py +0 -0
  89. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/dependencies/client_credential.py +0 -0
  90. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/dependencies/db.py +0 -0
  91. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/dependencies/uow.py +0 -0
  92. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/exception_handlers.py +0 -0
  93. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/exceptions.py +0 -0
  94. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/middleware/__init__.py +0 -0
  95. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/middleware/access_log.py +0 -0
  96. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/middleware/add_request_context.py +0 -0
  97. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/middleware/authentication.py +0 -0
  98. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/pagination/__init__.py +0 -0
  99. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/pagination/offset_base.py +0 -0
  100. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/routers.py +0 -0
  101. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/schema/__init__.py +0 -0
  102. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/schema/adaptive_schema.py +0 -0
  103. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/schema/base_response_model.py +0 -0
  104. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/api/schemas.py +0 -0
  105. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/cli/__init__.py +0 -0
  106. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/cli/crypto.py +0 -0
  107. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/cli/init_db.py +0 -0
  108. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/cli/main.py +0 -0
  109. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/cli/ticket.py +0 -0
  110. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/jobs/__init__.py +0 -0
  111. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/jobs/dispatch_outbox.py +0 -0
  112. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/interfaces/jobs/sync_tokens.py +0 -0
  113. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/types.py +0 -0
  114. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/__init__.py +0 -0
  115. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/cache.py +0 -0
  116. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/cache_keys.py +0 -0
  117. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/cache_registry.py +0 -0
  118. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/context.py +0 -0
  119. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/crypto.py +0 -0
  120. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/flatten_dict.py +0 -0
  121. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/flatten_sqlalchemy_result.py +0 -0
  122. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/http.py +0 -0
  123. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/normalize_mobile.py +0 -0
  124. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/read_files.py +0 -0
  125. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/redactor.py +0 -0
  126. {nlbone-0.11.0 → nlbone-0.11.1}/src/nlbone/utils/time.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nlbone
3
- Version: 0.11.0
3
+ Version: 0.11.1
4
4
  Summary: Backbone package for interfaces and infrastructure in Python projects
5
5
  Author-email: Amir Hosein Kahkbazzadeh <a.khakbazzadeh@gmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "nlbone"
7
- version = "0.11.0"
7
+ version = "0.11.1"
8
8
  description = "Backbone package for interfaces and infrastructure in Python projects"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,7 +1,8 @@
1
1
  from typing import Any, Callable, List, Optional, Sequence, Type, Union
2
2
 
3
- from sqlalchemy import and_, asc, case, desc, literal, or_
3
+ from sqlalchemy import Select, and_, asc, case, desc, func, literal, or_, select
4
4
  from sqlalchemy.dialects.postgresql import ENUM as PGEnum
5
+ from sqlalchemy.ext.asyncio import AsyncSession
5
6
  from sqlalchemy.orm import Query, Session, aliased
6
7
  from sqlalchemy.orm.attributes import InstrumentedAttribute
7
8
  from sqlalchemy.orm.interfaces import LoaderOption
@@ -384,6 +385,19 @@ def _apply_order(pagination: PaginateRequest, entity, query):
384
385
  return query
385
386
 
386
387
 
388
+ def apply_pagination_async(pagination: PaginateRequest, entity, stmt: Select = None, limit: bool = True) -> Select:
389
+ if stmt is None:
390
+ stmt = select(entity)
391
+
392
+ stmt = _apply_filters(pagination, entity, stmt)
393
+ stmt = _apply_order(pagination, entity, stmt)
394
+
395
+ if limit:
396
+ stmt = stmt.limit(pagination.limit).offset(pagination.offset)
397
+
398
+ return stmt
399
+
400
+
387
401
  def apply_pagination(pagination: PaginateRequest, entity, session: Session, limit=True, query=None) -> Query:
388
402
  if not query:
389
403
  query = session.query(entity)
@@ -437,6 +451,43 @@ def _serialize_item(item: Any, output_cls: OutputType) -> Any:
437
451
  return item
438
452
 
439
453
 
454
+ async def get_paginated_response_async(
455
+ pagination,
456
+ entity,
457
+ session: AsyncSession,
458
+ *,
459
+ with_count: bool = True,
460
+ output_cls: Optional[Type] = None,
461
+ eager_options: Optional[Sequence[LoaderOption]] = None,
462
+ query: Optional[Select] = None,
463
+ ) -> dict:
464
+ stmt = query if query is not None else select(entity)
465
+
466
+ if eager_options:
467
+ stmt = stmt.options(*eager_options)
468
+
469
+ filtered_stmt = apply_pagination_async(pagination, entity, stmt=stmt, limit=False)
470
+
471
+ total_count = None
472
+ if with_count:
473
+ subquery = filtered_stmt.subquery()
474
+ count_stmt = select(func.count()).select_from(subquery)
475
+ total_count = await session.scalar(count_stmt)
476
+
477
+ final_stmt = filtered_stmt.limit(pagination.limit).offset(pagination.offset)
478
+ result = await session.execute(final_stmt)
479
+ rows = result.scalars().all()
480
+
481
+ if output_cls is not None:
482
+ data = [output_cls.model_validate(r, from_attributes=True).model_dump() for r in rows]
483
+ else:
484
+ data = rows
485
+
486
+ return PaginateResponse(
487
+ total_count=total_count, data=data, limit=pagination.limit, offset=pagination.offset
488
+ ).to_dict()
489
+
490
+
440
491
  def get_paginated_response(
441
492
  pagination,
442
493
  entity,
@@ -2,11 +2,61 @@ import inspect
2
2
  from typing import Any, Dict
3
3
 
4
4
  from pydantic import BaseModel
5
+ from sqlalchemy.ext.asyncio import AsyncSession
5
6
 
6
7
  from nlbone.container import Container
7
8
  from nlbone.interfaces.api.additional_filed.field_registry import FieldRule, ResourceRegistry
8
9
 
9
10
 
11
+ async def assemble_response_async(
12
+ obj: Any,
13
+ reg: ResourceRegistry,
14
+ selected_rules: Dict[str, FieldRule],
15
+ session: AsyncSession,
16
+ base_schema: type[BaseModel] | None,
17
+ scope_map: dict[str, set[str]] = None,
18
+ **kwargs,
19
+ ) -> Dict[str, Any]:
20
+ """
21
+ Async version of assemble_response.
22
+ Awaits loaders if they are coroutines.
23
+ """
24
+ base = {f: getattr(obj, f, None) for f in reg.default_fields - set(reg.rules.keys())}
25
+ if base_schema:
26
+ base = base_schema.model_validate(base).model_dump()
27
+
28
+ ctx = {
29
+ "file_service": Container.afiles_service(),
30
+ "entity": obj,
31
+ "db": session,
32
+ "pricing_service": Container.async_pricing_service(),
33
+ **kwargs,
34
+ }
35
+
36
+ roots = {name.split(".", 1)[0] for name in selected_rules.keys()}
37
+
38
+ for root in roots:
39
+ rule = reg.rules.get(root)
40
+ if not rule:
41
+ continue
42
+
43
+ if rule.loader:
44
+ dependencies = ctx | {"scope": scope_map.get(root, {""})} if scope_map else ctx
45
+
46
+ result = inject_dependencies(rule.loader, dependencies=dependencies)
47
+
48
+ if inspect.iscoroutine(result):
49
+ value = await result
50
+ else:
51
+ value = result
52
+ else:
53
+ value = _get_nested_attr(obj, root)
54
+
55
+ _put_nested_key(base, root, value)
56
+
57
+ return base
58
+
59
+
10
60
  def assemble_response(
11
61
  obj: Any,
12
62
  reg: ResourceRegistry,
@@ -1,10 +1,11 @@
1
1
  from dataclasses import dataclass, field
2
2
  from enum import Enum
3
- from typing import Any, Callable, Dict, Iterable, Optional, Set, Tuple, Type
3
+ from typing import Any, Awaitable, Callable, Dict, Iterable, Optional, Set, Tuple, Type
4
4
 
5
5
  from pydantic import BaseModel
6
6
 
7
7
  PermissionChecker = Callable[[Any, str], bool]
8
+ AsyncPermissionChecker = Callable[[str], Awaitable[bool]]
8
9
  Loader = Callable
9
10
 
10
11
 
@@ -2,11 +2,15 @@ from collections import defaultdict
2
2
  from typing import Dict, List, Set, Tuple
3
3
 
4
4
  from nlbone.interfaces.api.additional_filed.field_registry import (
5
+ AsyncPermissionChecker,
5
6
  DefaultsMergeMode,
6
7
  FieldRule,
7
8
  PermissionChecker,
8
9
  ResourceRegistry,
9
10
  )
11
+ from nlbone.interfaces.api.dependencies.async_auth import (
12
+ client_or_user_has_access_func as async_client_or_user_has_access_func,
13
+ )
10
14
  from nlbone.interfaces.api.dependencies.auth import client_or_user_has_access_func
11
15
  from nlbone.interfaces.api.exceptions import BadRequestException, InternalServerException
12
16
 
@@ -36,6 +40,81 @@ class AdditionalFieldsRequest:
36
40
  return [x.strip() for x in s.split(",") if x.strip()]
37
41
 
38
42
 
43
+ async def resolve_requested_fields_async(
44
+ reg: ResourceRegistry,
45
+ additional_fields: AdditionalFieldsRequest = None,
46
+ can: AsyncPermissionChecker = None,
47
+ mode: DefaultsMergeMode = DefaultsMergeMode.UNION,
48
+ ) -> Tuple[Set[str], Dict[str, FieldRule]]:
49
+ if not additional_fields:
50
+ additional_fields = AdditionalFieldsRequest()
51
+ if not can:
52
+ can = async_client_or_user_has_access_func
53
+
54
+ reg.finalize_defaults(mode=mode)
55
+
56
+ expanded_bundles = _expand_bundles(reg, set(additional_fields.bundles))
57
+ requested = set(reg.default_fields) | set(additional_fields.fields) | expanded_bundles
58
+
59
+ # Validation
60
+ unknown = {f for f in requested if (f not in reg.default_fields and f not in reg.rules)}
61
+ if unknown:
62
+ raise BadRequestException(f"unknown_fields: {sorted(unknown)}")
63
+
64
+ selected_rules: Dict[str, FieldRule] = {}
65
+
66
+ for f in requested:
67
+ rule = reg.rules.get(f)
68
+
69
+ if f in reg.default_fields and f not in reg.rules:
70
+ continue
71
+
72
+ if not rule:
73
+ continue
74
+
75
+ # Check Permission
76
+ if rule.permission:
77
+ try:
78
+ if not await can(rule.permission):
79
+ continue
80
+ except Exception:
81
+ continue
82
+
83
+ is_explicit = f in additional_fields.fields
84
+ if not rule.default and not is_explicit:
85
+ continue
86
+
87
+ selected_rules[f] = rule
88
+
89
+ # Dependencies
90
+ final_set = set(requested)
91
+
92
+ def add_deps_recursive(name: str):
93
+ _rule = reg.rules.get(name)
94
+ if not _rule or not _rule.deps:
95
+ return
96
+ for d in _rule.deps:
97
+ if d not in final_set:
98
+ final_set.add(d)
99
+ add_deps_recursive(d)
100
+
101
+ for field_with_rule in list(selected_rules.keys()):
102
+ add_deps_recursive(field_with_rule)
103
+
104
+ parents = {f.split(".", 1)[0] for f in final_set if "." in f}
105
+ for p in parents:
106
+ if p in reg.rules:
107
+ final_set.add(p)
108
+ if p not in selected_rules:
109
+ selected_rules[p] = reg.rules[p]
110
+
111
+ missing_deps = (final_set - set(reg.default_fields)) - set(reg.rules.keys())
112
+ if missing_deps:
113
+ raise InternalServerException(f"registry_missing_rules_for: {sorted(missing_deps)}")
114
+
115
+ return final_set, selected_rules
116
+
117
+
39
118
  def resolve_requested_fields(
40
119
  reg: ResourceRegistry,
41
120
  additional_fields: AdditionalFieldsRequest = None,
@@ -92,7 +92,7 @@ def client_or_user_has_access(*, permissions=None, client_permissions=None):
92
92
  return decorator
93
93
 
94
94
 
95
- async def is_permitted_user(permissions=None):
95
+ def is_permitted_user(permissions=None):
96
96
  async def check_permissions():
97
97
  try:
98
98
  if bypass_authz():
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes