GeneralManager 0.12.2__tar.gz → 0.13.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 (65) hide show
  1. {generalmanager-0.12.2 → generalmanager-0.13.1}/GeneralManager.egg-info/PKG-INFO +1 -1
  2. {generalmanager-0.12.2 → generalmanager-0.13.1}/PKG-INFO +1 -1
  3. {generalmanager-0.12.2 → generalmanager-0.13.1}/pyproject.toml +1 -1
  4. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/api/graphql.py +8 -2
  5. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/bucket/baseBucket.py +18 -9
  6. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/bucket/calculationBucket.py +31 -17
  7. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/bucket/databaseBucket.py +14 -4
  8. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/bucket/groupBucket.py +12 -2
  9. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/interface/databaseInterface.py +7 -4
  10. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/permission/managerBasedPermission.py +59 -10
  11. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/permission/permissionChecks.py +5 -0
  12. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/utils/pathMapping.py +12 -11
  13. {generalmanager-0.12.2 → generalmanager-0.13.1}/GeneralManager.egg-info/SOURCES.txt +0 -0
  14. {generalmanager-0.12.2 → generalmanager-0.13.1}/GeneralManager.egg-info/dependency_links.txt +0 -0
  15. {generalmanager-0.12.2 → generalmanager-0.13.1}/GeneralManager.egg-info/requires.txt +0 -0
  16. {generalmanager-0.12.2 → generalmanager-0.13.1}/GeneralManager.egg-info/top_level.txt +0 -0
  17. {generalmanager-0.12.2 → generalmanager-0.13.1}/LICENSE +0 -0
  18. {generalmanager-0.12.2 → generalmanager-0.13.1}/README.md +0 -0
  19. {generalmanager-0.12.2 → generalmanager-0.13.1}/setup.cfg +0 -0
  20. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/__init__.py +0 -0
  21. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/api/mutation.py +0 -0
  22. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/api/property.py +0 -0
  23. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/apps.py +0 -0
  24. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/cache/cacheDecorator.py +0 -0
  25. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/cache/cacheTracker.py +0 -0
  26. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/cache/dependencyIndex.py +0 -0
  27. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/cache/modelDependencyCollector.py +0 -0
  28. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/cache/signals.py +0 -0
  29. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/factory/__init__.py +0 -0
  30. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/factory/autoFactory.py +0 -0
  31. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/factory/factories.py +0 -0
  32. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/factory/factoryMethods.py +0 -0
  33. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/interface/__init__.py +0 -0
  34. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/interface/baseInterface.py +0 -0
  35. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/interface/calculationInterface.py +0 -0
  36. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/interface/databaseBasedInterface.py +0 -0
  37. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/interface/models.py +0 -0
  38. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/interface/readOnlyInterface.py +0 -0
  39. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/manager/__init__.py +0 -0
  40. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/manager/generalManager.py +0 -0
  41. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/manager/groupManager.py +0 -0
  42. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/manager/input.py +0 -0
  43. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/manager/meta.py +0 -0
  44. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/measurement/__init__.py +0 -0
  45. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/measurement/measurement.py +0 -0
  46. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/measurement/measurementField.py +0 -0
  47. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/permission/__init__.py +0 -0
  48. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/permission/basePermission.py +0 -0
  49. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/permission/fileBasedPermission.py +0 -0
  50. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/permission/mutationPermission.py +0 -0
  51. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/permission/permissionDataManager.py +0 -0
  52. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/permission/utils.py +0 -0
  53. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/rule/__init__.py +0 -0
  54. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/rule/handler.py +0 -0
  55. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/rule/rule.py +0 -0
  56. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/utils/__init__.py +0 -0
  57. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/utils/argsToKwargs.py +0 -0
  58. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/utils/filterParser.py +0 -0
  59. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/utils/formatString.py +0 -0
  60. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/utils/jsonEncoder.py +0 -0
  61. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/utils/makeCacheKey.py +0 -0
  62. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/utils/noneToZero.py +0 -0
  63. {generalmanager-0.12.2 → generalmanager-0.13.1}/src/general_manager/utils/testing.py +0 -0
  64. {generalmanager-0.12.2 → generalmanager-0.13.1}/tests/test_settings.py +0 -0
  65. {generalmanager-0.12.2 → generalmanager-0.13.1}/tests/test_urls.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.12.2
3
+ Version: 0.13.1
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.12.2
3
+ Version: 0.13.1
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "GeneralManager"
7
- version = "0.12.2"
7
+ version = "0.13.1"
8
8
  description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
@@ -384,10 +384,16 @@ class GraphQL:
384
384
  info: GraphQLResolveInfo,
385
385
  ) -> Bucket:
386
386
  """
387
- Wendet die vom Permission-Interface vorgegebenen Filter auf das Queryset an.
387
+ Applies permission-based filters to a queryset according to the permission interface of the given manager class.
388
+
389
+ Returns:
390
+ A queryset containing only the items allowed by the user's read permissions. If no permission filters are defined, returns the original queryset unchanged.
388
391
  """
389
392
  permission_filters = getReadPermissionFilter(general_manager_class, info)
390
- filtered_queryset = queryset
393
+ if not permission_filters:
394
+ return queryset
395
+
396
+ filtered_queryset = queryset.none()
391
397
  for perm_filter, perm_exclude in permission_filters:
392
398
  qs_perm = queryset.exclude(**perm_exclude).filter(**perm_filter)
393
399
  filtered_queryset = filtered_queryset | qs_perm
@@ -61,13 +61,13 @@ class Bucket(ABC, Generic[GeneralManagerType]):
61
61
  other: Bucket[GeneralManagerType] | GeneralManagerType,
62
62
  ) -> Bucket[GeneralManagerType]:
63
63
  """
64
- Return a new bucket containing the union of this bucket and another bucket or manager instance.
64
+ Return a new bucket representing the union of this bucket and another bucket or a single manager instance.
65
65
 
66
66
  Parameters:
67
- other: Another bucket or a single manager instance to combine with this bucket.
67
+ other: Another bucket or a single manager instance to include in the union.
68
68
 
69
69
  Returns:
70
- A new bucket containing all unique items from both sources.
70
+ A new bucket containing all unique items from both this bucket and the provided argument.
71
71
  """
72
72
  raise NotImplementedError
73
73
 
@@ -229,14 +229,23 @@ class Bucket(ABC, Generic[GeneralManagerType]):
229
229
 
230
230
  def group_by(self, *group_by_keys: str) -> GroupBucket[GeneralManagerType]:
231
231
  """
232
- Groups the bucket's data by one or more specified keys.
233
-
234
- Args:
235
- *group_by_keys: One or more attribute names to group the data by.
236
-
232
+ Return a GroupBucket that groups the items in this bucket by the specified attribute keys.
233
+
234
+ Parameters:
235
+ *group_by_keys (str): Attribute names to group the items by.
236
+
237
237
  Returns:
238
- A GroupBucket instance containing the grouped data.
238
+ GroupBucket[GeneralManagerType]: A bucket containing items grouped by the given keys.
239
239
  """
240
240
  from general_manager.bucket.groupBucket import GroupBucket
241
241
 
242
242
  return GroupBucket(self._manager_class, group_by_keys, self)
243
+
244
+ def none(self) -> Bucket[GeneralManagerType]:
245
+ """
246
+ Raise NotImplementedError to indicate that subclasses must implement a method returning an empty bucket.
247
+ """
248
+ raise NotImplementedError(
249
+ "The 'none' method is not implemented in the base Bucket class. "
250
+ "Subclasses should implement this method to return an empty bucket."
251
+ )
@@ -9,6 +9,7 @@ from typing import (
9
9
  Generator,
10
10
  List,
11
11
  )
12
+ from copy import deepcopy
12
13
  from general_manager.interface.baseInterface import (
13
14
  generalManagerClassName,
14
15
  GeneralManagerType,
@@ -95,13 +96,13 @@ class CalculationBucket(Bucket[GeneralManagerType]):
95
96
  other: Bucket[GeneralManagerType] | GeneralManagerType,
96
97
  ) -> CalculationBucket[GeneralManagerType]:
97
98
  """
98
- Combine this CalculationBucket with another bucket or manager instance of the same manager class.
99
+ Combine this CalculationBucket with another bucket or a manager instance of the same manager class.
99
100
 
100
- If combined with a manager instance, returns a bucket filtered to that manager's identification. If combined with another CalculationBucket of the same manager class, returns a new bucket containing only the filters and excludes that are present and identical in both buckets.
101
+ If combined with a manager instance, returns a bucket filtered to that instance's identification. If combined with another CalculationBucket of the same manager class, returns a new bucket containing only the filters and excludes that are present and identical in both buckets.
101
102
 
102
103
  Raises:
103
104
  ValueError: If the other object is not a CalculationBucket or manager of the same class.
104
-
105
+
105
106
  Returns:
106
107
  CalculationBucket[GeneralManagerType]: A new CalculationBucket representing the intersection of filters and excludes, or a filtered bucket for the given manager instance.
107
108
  """
@@ -183,17 +184,18 @@ class CalculationBucket(Bucket[GeneralManagerType]):
183
184
 
184
185
  def all(self) -> CalculationBucket:
185
186
  """
186
- Returns the current CalculationBucket instance.
187
-
188
- This method allows for compatibility with interfaces expecting an `all()` method that returns the full set of items.
187
+ Return a deep copy of the current CalculationBucket instance.
188
+
189
+ Use this method to obtain an independent copy of the bucket, ensuring that modifications to the returned instance do not affect the original.
189
190
  """
190
- return self
191
+ return deepcopy(self)
191
192
 
192
193
  def __iter__(self) -> Generator[GeneralManagerType, None, None]:
193
194
  """
194
- Yields manager instances for each valid combination of input parameters.
195
-
196
- Iterates over all generated input combinations, instantiating the manager class with each set of parameters.
195
+ Iterate over all valid input combinations, yielding a manager instance for each.
196
+
197
+ Yields:
198
+ Manager instances created with each valid set of input parameters.
197
199
  """
198
200
  combinations = self.generate_combinations()
199
201
  for combo in combinations:
@@ -470,15 +472,27 @@ class CalculationBucket(Bucket[GeneralManagerType]):
470
472
  self, key: str | tuple[str], reverse: bool = False
471
473
  ) -> CalculationBucket[GeneralManagerType]:
472
474
  """
473
- Returns a new CalculationBucket with updated sorting parameters.
474
-
475
- Args:
476
- key: The field name or tuple of field names to sort combinations by.
477
- reverse: If True, sorts in descending order.
478
-
475
+ Return a new CalculationBucket instance with updated sorting criteria.
476
+
477
+ Parameters:
478
+ key (str or tuple of str): Field name(s) to sort the combinations by.
479
+ reverse (bool): Whether to sort in descending order.
480
+
479
481
  Returns:
480
- A new CalculationBucket instance with the specified sorting applied.
482
+ CalculationBucket: A new bucket instance sorted according to the specified key and order.
481
483
  """
482
484
  return CalculationBucket(
483
485
  self._manager_class, self.filters, self.excludes, key, reverse
484
486
  )
487
+
488
+ def none(self) -> CalculationBucket[GeneralManagerType]:
489
+ """
490
+ Return a new CalculationBucket instance of the same type containing no items.
491
+
492
+ The returned bucket has all filters, excludes, and cached combinations cleared, representing an empty set of combinations.
493
+ """
494
+ own = self.all()
495
+ own._current_combinations = None
496
+ own.filters = {}
497
+ own.excludes = {}
498
+ return own
@@ -211,14 +211,14 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
211
211
  reverse: bool = False,
212
212
  ) -> DatabaseBucket:
213
213
  """
214
- Return a new DatabaseBucket sorted by the specified field or fields.
215
-
214
+ Return a new DatabaseBucket with items sorted by the specified field or fields.
215
+
216
216
  Parameters:
217
217
  key (str or tuple of str): Field name or tuple of field names to sort by.
218
218
  reverse (bool): If True, sort in descending order.
219
-
219
+
220
220
  Returns:
221
- DatabaseBucket: A new bucket instance containing the sorted queryset.
221
+ DatabaseBucket: A new bucket containing the sorted items.
222
222
  """
223
223
  if isinstance(key, str):
224
224
  key = (key,)
@@ -227,3 +227,13 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
227
227
  else:
228
228
  sorted_data = self._data.order_by(*key)
229
229
  return self.__class__(sorted_data, self._manager_class)
230
+
231
+ def none(self) -> DatabaseBucket[GeneralManagerType]:
232
+ """
233
+ Return a new DatabaseBucket instance of the same type containing no items.
234
+
235
+ This method creates an empty bucket while preserving the current manager class and bucket type.
236
+ """
237
+ own = self.all()
238
+ own._data = own._data.none()
239
+ return own
@@ -285,8 +285,8 @@ class GroupBucket(Bucket[GeneralManagerType]):
285
285
 
286
286
  def group_by(self, *group_by_keys: str) -> GroupBucket[GeneralManagerType]:
287
287
  """
288
- Returns a new GroupBucket grouped by the current and additional specified attribute keys.
289
-
288
+ Return a new GroupBucket grouped by the current and additional attribute keys.
289
+
290
290
  Additional group-by keys are appended to the existing grouping, and the new GroupBucket is constructed from the same underlying data.
291
291
  """
292
292
  return GroupBucket(
@@ -294,3 +294,13 @@ class GroupBucket(Bucket[GeneralManagerType]):
294
294
  tuple([*self._group_by_keys, *group_by_keys]),
295
295
  self._basis_data,
296
296
  )
297
+
298
+ def none(self) -> GroupBucket[GeneralManagerType]:
299
+ """
300
+ Return a new empty GroupBucket with the same manager class and group-by keys as the current instance.
301
+
302
+ This method creates a GroupBucket containing no items, preserving the grouping configuration of the original.
303
+ """
304
+ return GroupBucket(
305
+ self._manager_class, self._group_by_keys, self._basis_data.none()
306
+ )
@@ -84,14 +84,17 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
84
84
  instance: GeneralManagerModel, many_to_many_kwargs: dict[str, list[Any]]
85
85
  ) -> GeneralManagerModel:
86
86
  """
87
- Set many-to-many relationship fields on a model instance using provided values.
88
-
89
- Converts lists of `GeneralManager` instances to their corresponding IDs before updating the relationships. Returns the updated instance.
87
+ Set many-to-many relationship fields on a model instance using the provided values.
88
+
89
+ For each field, converts lists of `GeneralManager` instances to their IDs if necessary, and updates the corresponding many-to-many relationship on the instance. Fields with values of `None` or `NOT_PROVIDED` are ignored.
90
+
91
+ Returns:
92
+ GeneralManagerModel: The updated model instance with many-to-many relationships set.
90
93
  """
91
94
  from general_manager.manager.generalManager import GeneralManager
92
95
 
93
96
  for key, value in many_to_many_kwargs.items():
94
- if not value:
97
+ if value is None or value is NOT_PROVIDED:
95
98
  continue
96
99
  field_name = key.removesuffix("_id_list")
97
100
  if isinstance(value, list) and all(
@@ -17,20 +17,30 @@ type permission_type = Literal[
17
17
  ]
18
18
 
19
19
 
20
+ class notExistent:
21
+ pass
22
+
23
+
20
24
  class ManagerBasedPermission(BasePermission):
21
25
  __based_on__: Optional[str] = None
22
- __read__: list[str] = ["public"]
23
- __create__: list[str] = ["isAuthenticated"]
24
- __update__: list[str] = ["isAuthenticated"]
25
- __delete__: list[str] = ["isAuthenticated"]
26
+ __read__: list[str]
27
+ __create__: list[str]
28
+ __update__: list[str]
29
+ __delete__: list[str]
26
30
 
27
31
  def __init__(
28
32
  self,
29
33
  instance: PermissionDataManager | GeneralManager,
30
34
  request_user: AbstractUser,
31
35
  ) -> None:
32
-
36
+ """
37
+ Initializes the ManagerBasedPermission with a manager instance and the requesting user.
38
+
39
+ Configures default CRUD permissions, collects attribute-specific permissions, and sets up any related "based on" permission for cascading checks.
40
+ """
33
41
  super().__init__(instance, request_user)
42
+ self.__setPermissions()
43
+
34
44
  self.__attribute_permissions = self.__getAttributePermissions()
35
45
  self.__based_on_permission = self.__getBasedOnPermission()
36
46
  self.__overall_results: Dict[permission_type, Optional[bool]] = {
@@ -40,20 +50,52 @@ class ManagerBasedPermission(BasePermission):
40
50
  "delete": None,
41
51
  }
42
52
 
53
+ def __setPermissions(self, skip_based_on: bool = False) -> None:
54
+
55
+ """
56
+ Assigns default permission lists for CRUD actions based on the presence of a related permission attribute.
57
+
58
+ If the permission is based on another attribute and `skip_based_on` is False, all default permissions are set to empty lists. Otherwise, read permissions default to `["public"]` and write permissions to `["isAuthenticated"]`. Class-level overrides are respected if present.
59
+ """
60
+ default_read = ["public"]
61
+ default_write = ["isAuthenticated"]
62
+
63
+ if self.__based_on__ is not None and not skip_based_on:
64
+ default_read = []
65
+ default_write = []
66
+
67
+ self.__read__ = getattr(self.__class__, "__read__", default_read)
68
+ self.__create__ = getattr(self.__class__, "__create__", default_write)
69
+ self.__update__ = getattr(self.__class__, "__update__", default_write)
70
+ self.__delete__ = getattr(self.__class__, "__delete__", default_write)
71
+
43
72
  def __getBasedOnPermission(self) -> Optional[BasePermission]:
73
+ """
74
+ Retrieves the permission object associated with the `__based_on__` attribute, if present and valid.
75
+
76
+ Returns:
77
+ An instance of the related `BasePermission` subclass if the `__based_on__` attribute exists on the instance and its `Permission` class is a subclass of `BasePermission`; otherwise, returns `None`.
78
+
79
+ Raises:
80
+ ValueError: If the `__based_on__` attribute is missing from the instance.
81
+ TypeError: If the `__based_on__` attribute is not a `GeneralManager` or its subclass.
82
+ """
44
83
  from general_manager.manager.generalManager import GeneralManager
45
84
 
46
85
  __based_on__ = getattr(self, "__based_on__")
47
86
  if __based_on__ is None:
48
87
  return None
49
88
 
50
- basis_object = getattr(self.instance, __based_on__, None)
51
- if basis_object is None:
89
+ basis_object = getattr(self.instance, __based_on__, notExistent)
90
+ if basis_object is notExistent:
52
91
  raise ValueError(
53
- f"Based on object {__based_on__} not found in instance {self.instance}"
92
+ f"Based on configuration '{__based_on__}' is not valid or does not exist."
54
93
  )
55
- if not isinstance(basis_object, GeneralManager) and not issubclass(
56
- basis_object, GeneralManager
94
+ if basis_object is None:
95
+ self.__setPermissions(skip_based_on=True)
96
+ return None
97
+ if not isinstance(basis_object, GeneralManager) and not (
98
+ isinstance(basis_object, type) and issubclass(basis_object, GeneralManager)
57
99
  ):
58
100
  raise TypeError(f"Based on object {__based_on__} is not a GeneralManager")
59
101
 
@@ -124,6 +166,13 @@ class ManagerBasedPermission(BasePermission):
124
166
  self,
125
167
  permissions: list[str],
126
168
  ) -> bool:
169
+ """
170
+ Return True if no permissions are required or if at least one permission string is valid for the user.
171
+
172
+ If the permissions list is empty, access is granted. Otherwise, returns True if any permission string in the list is validated for the user; returns False if none are valid.
173
+ """
174
+ if not permissions:
175
+ return True
127
176
  for permission in permissions:
128
177
  if self.validatePermissionString(permission):
129
178
  return True
@@ -34,6 +34,11 @@ permission_functions: dict[str, PermissionDict] = {
34
34
  "permission_method": lambda instance, user, config: True,
35
35
  "permission_filter": lambda user, config: None,
36
36
  },
37
+ "matches": {
38
+ "permission_method": lambda instance, user, config: getattr(instance, config[0])
39
+ == config[1],
40
+ "permission_filter": lambda user, config: {"filter": {config[0]: config[1]}},
41
+ },
37
42
  "ends_with": {
38
43
  "permission_method": lambda instance, user, config: getattr(
39
44
  instance, config[0]
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, cast, get_args
3
3
  from general_manager.manager.meta import GeneralManagerMeta
4
4
  from general_manager.api.property import GraphQLProperty
5
5
 
6
- from general_manager.interface.baseInterface import Bucket
6
+ from general_manager.bucket.baseBucket import Bucket
7
7
  from general_manager.manager.generalManager import GeneralManager
8
8
 
9
9
 
@@ -26,7 +26,7 @@ class PathMap:
26
26
  def createPathMapping(cls):
27
27
  """
28
28
  Builds the mapping of paths between all pairs of distinct managed classes.
29
-
29
+
30
30
  Iterates over all registered managed classes and creates a PathTracer for each unique start and destination class pair, storing them in the mapping dictionary.
31
31
  """
32
32
  all_managed_classes = GeneralManagerMeta.all_classes
@@ -38,10 +38,9 @@ class PathMap:
38
38
  ] = PathTracer(start_class, destination_class)
39
39
 
40
40
  def __init__(self, path_start: PathStart | GeneralManager | type[GeneralManager]):
41
-
42
41
  """
43
42
  Initializes a PathMap with a specified starting point.
44
-
43
+
45
44
  The starting point can be a class name (string), a GeneralManager instance, or a GeneralManager subclass. Sets internal attributes for the start instance, class, and class name based on the input.
46
45
  """
47
46
  if isinstance(path_start, GeneralManager):
@@ -86,9 +85,11 @@ class PathMap:
86
85
  Returns a list of all classes that are connected to the start class.
87
86
  """
88
87
  connected_classes: set[str] = set()
89
- for path_tuple in self.mapping.keys():
88
+ for path_tuple, path_obj in self.mapping.items():
90
89
  if path_tuple[0] == self.start_class_name:
91
90
  destination_class_name = path_tuple[1]
91
+ if path_obj.path is None:
92
+ continue
92
93
  connected_classes.add(destination_class_name)
93
94
  return connected_classes
94
95
 
@@ -107,14 +108,13 @@ class PathTracer:
107
108
  def createPath(
108
109
  self, current_manager: type[GeneralManager], path: list[str]
109
110
  ) -> list[str] | None:
110
-
111
111
  """
112
112
  Recursively constructs a path of attribute names from the current manager class to the destination class.
113
-
113
+
114
114
  Args:
115
115
  current_manager: The current GeneralManager subclass being inspected.
116
116
  path: The list of attribute names traversed so far.
117
-
117
+
118
118
  Returns:
119
119
  A list of attribute names representing the path to the destination class, or None if no path exists.
120
120
  """
@@ -135,6 +135,8 @@ class PathTracer:
135
135
  for attr, attr_type in current_connections.items():
136
136
  if attr in path or attr_type == self.start_class:
137
137
  continue
138
+ if attr_type is None:
139
+ continue
138
140
  if not issubclass(attr_type, GeneralManager):
139
141
  continue
140
142
  if attr_type == self.destination_class:
@@ -148,13 +150,12 @@ class PathTracer:
148
150
  def traversePath(
149
151
  self, start_instance: GeneralManager | Bucket
150
152
  ) -> GeneralManager | Bucket | None:
151
-
152
153
  """
153
154
  Traverses the stored path from a starting instance to reach the destination instance or bucket.
154
-
155
+
155
156
  Args:
156
157
  start_instance: The initial GeneralManager or Bucket instance from which to begin traversal.
157
-
158
+
158
159
  Returns:
159
160
  The resulting GeneralManager or Bucket instance at the end of the path, or None if the path is empty.
160
161
  """
File without changes