simple-module-feature-flags 0.0.40__tar.gz → 0.0.42__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 (40) hide show
  1. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/PKG-INFO +4 -4
  2. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/invalidation.py +8 -1
  3. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/module.py +6 -0
  4. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/package.json +4 -4
  5. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/pyproject.toml +4 -4
  6. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/tests/test_feature_flags_tenant_cleanup.py +35 -0
  7. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/.gitignore +0 -0
  8. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/LICENSE +0 -0
  9. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/README.md +0 -0
  10. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/__init__.py +0 -0
  11. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/constants.py +0 -0
  12. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/contracts/__init__.py +0 -0
  13. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/contracts/schemas.py +0 -0
  14. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/deps.py +0 -0
  15. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/endpoints/__init__.py +0 -0
  16. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/endpoints/api.py +0 -0
  17. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/endpoints/views.py +0 -0
  18. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/locales/de.json +0 -0
  19. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/locales/en.json +0 -0
  20. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/models.py +0 -0
  21. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/pages/Browse.tsx +0 -0
  22. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/pages/components/FlagTable.tsx +0 -0
  23. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/pages/components/ScopeCard.tsx +0 -0
  24. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/pages/components/TenantPicker.tsx +0 -0
  25. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/pages/components/ToggleConfirmDialog.tsx +0 -0
  26. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/pages/types.ts +0 -0
  27. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/py.typed +0 -0
  28. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/service.py +0 -0
  29. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/feature_flags/tenant_cleanup.py +0 -0
  30. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/tests/conftest.py +0 -0
  31. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/tests/test_feature_flags_api.py +0 -0
  32. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/tests/test_feature_flags_audit_links.py +0 -0
  33. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/tests/test_feature_flags_commit_invalidation.py +0 -0
  34. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/tests/test_feature_flags_menu_section.py +0 -0
  35. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/tests/test_feature_flags_service.py +0 -0
  36. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/tests/test_feature_flags_tenancy.py +0 -0
  37. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/tests/test_flag_description_i18n.py +0 -0
  38. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/tests/test_store_outage_fallback.py +0 -0
  39. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/tests-js/feature_flags_Browse.test.tsx +0 -0
  40. {simple_module_feature_flags-0.0.40 → simple_module_feature_flags-0.0.42}/tsconfig.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: simple_module_feature_flags
3
- Version: 0.0.40
3
+ Version: 0.0.42
4
4
  Summary: Simple feature-flag module with per-tenant overrides and a consumer API for simple_module
5
5
  Project-URL: Homepage, https://github.com/antosubash/simple_module_python
6
6
  Project-URL: Repository, https://github.com/antosubash/simple_module_python
@@ -21,9 +21,9 @@ Classifier: Topic :: Internet :: WWW/HTTP
21
21
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
22
22
  Classifier: Typing :: Typed
23
23
  Requires-Python: >=3.12
24
- Requires-Dist: simple-module-core==0.0.40
25
- Requires-Dist: simple-module-db==0.0.40
26
- Requires-Dist: simple-module-hosting==0.0.40
24
+ Requires-Dist: simple-module-core==0.0.42
25
+ Requires-Dist: simple-module-db==0.0.42
26
+ Requires-Dist: simple-module-hosting==0.0.42
27
27
  Description-Content-Type: text/markdown
28
28
 
29
29
  # simple_module_feature_flags
@@ -1,8 +1,9 @@
1
1
  """Re-read flag overrides after commit or a remote invalidation."""
2
2
 
3
3
  from fastapi import FastAPI
4
- from simple_module_core.invalidation import InvalidationBus
4
+ from simple_module_core.invalidation import Invalidation, InvalidationBus
5
5
  from simple_module_core.invalidation_refresh import InvalidationRefresh
6
+ from simple_module_core.tenant_events import TENANT_DELETED_CHANNEL
6
7
 
7
8
  from feature_flags.service import FeatureFlagService
8
9
 
@@ -10,6 +11,12 @@ CHANNEL = "feature_flags.overrides"
10
11
 
11
12
 
12
13
  def subscribe(bus: InvalidationBus, app: FastAPI) -> InvalidationRefresh:
14
+ def forget_tenant(message: Invalidation) -> None:
15
+ if message.key is not None:
16
+ app.state.sm.feature_flags.clear_tenant_overrides(message.key)
17
+
18
+ bus.subscribe(TENANT_DELETED_CHANNEL, forget_tenant)
19
+
13
20
  async def reload(_key: str | None) -> None:
14
21
  async with app.state.sm.db.session_factory() as db:
15
22
  await FeatureFlagService(db).hydrate_registry(app.state.sm.feature_flags)
@@ -75,6 +75,12 @@ class FeatureFlagsModule(ModuleBase):
75
75
  from feature_flags.tenant_cleanup import purge_tenant_data
76
76
 
77
77
  bus.subscribe_durable(TenantDeleted, purge_tenant_data)
78
+ if app is not None:
79
+
80
+ async def forget_tenant(event: TenantDeleted) -> None:
81
+ app.state.sm.feature_flags.clear_tenant_overrides(event.tenant_id)
82
+
83
+ bus.subscribe(TenantDeleted, forget_tenant)
78
84
 
79
85
  def register_invalidations(self, bus: InvalidationBus, app: FastAPI) -> None:
80
86
  from feature_flags.invalidation import subscribe
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@simple-module-py/feature_flags",
3
- "version": "0.0.40",
3
+ "version": "0.0.42",
4
4
  "private": true,
5
5
  "description": "Frontend assets for the FeatureFlags module",
6
6
  "peerDependencies": {
7
+ "@inertiajs/react": "^3.9.1",
8
+ "@simple-module-py/ui": "*",
7
9
  "react": "^19.0.0",
8
- "react-dom": "^19.0.0",
9
- "@inertiajs/react": "^3.7.0",
10
- "@simple-module-py/ui": "*"
10
+ "react-dom": "^19.3.1"
11
11
  },
12
12
  "devDependencies": {
13
13
  "@simple-module-py/tsconfig": "*"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "simple_module_feature_flags"
3
- version = "0.0.40"
3
+ version = "0.0.42"
4
4
  description = "Simple feature-flag module with per-tenant overrides and a consumer API for simple_module"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -21,9 +21,9 @@ classifiers = [
21
21
  "Typing :: Typed",
22
22
  ]
23
23
  dependencies = [
24
- "simple_module_core==0.0.40",
25
- "simple_module_db==0.0.40",
26
- "simple_module_hosting==0.0.40",
24
+ "simple_module_core==0.0.42",
25
+ "simple_module_db==0.0.42",
26
+ "simple_module_hosting==0.0.42",
27
27
  ]
28
28
 
29
29
  [project.entry-points.simple_module]
@@ -32,3 +32,38 @@ async def test_durable_tenant_cleanup_is_scoped_idempotent_and_transactional(db_
32
32
  (SCOPE_TENANT, "other"),
33
33
  (SCOPE_SYSTEM, "deleted"),
34
34
  }
35
+
36
+
37
+ async def test_feature_flags_tenant_event_clears_only_deleted_tenant():
38
+ from types import SimpleNamespace
39
+
40
+ from fastapi import FastAPI
41
+ from simple_module_core.feature_flags import FeatureFlagRegistry
42
+
43
+ app = FastAPI()
44
+ registry = FeatureFlagRegistry()
45
+ app.state.sm = SimpleNamespace(feature_flags=registry)
46
+ registry.set_override("unknown.flag", True, tenant_id="deleted")
47
+ registry.set_override("unknown.flag", True, tenant_id="other")
48
+ registry.set_override("unknown.flag", False)
49
+ bus = EventBus()
50
+ FeatureFlagsModule().register_event_handlers(bus, app)
51
+ event = TenantDeleted(tenant_id="deleted", slug="deleted", name="Deleted")
52
+ await bus.publish(event)
53
+ await bus.publish(event)
54
+ assert registry.tenant_override("unknown.flag", "deleted") is None
55
+ assert registry.tenant_override("unknown.flag", "other") is True
56
+ assert registry.system_override("unknown.flag") is False
57
+
58
+
59
+ async def test_feature_flags_remote_tenant_deletion_clears_registry(app):
60
+ from simple_module_core.invalidation import Invalidation
61
+ from simple_module_core.tenant_events import TENANT_DELETED_CHANNEL
62
+
63
+ registry = app.state.sm.feature_flags
64
+ registry.set_override("unknown.flag", True, tenant_id="deleted")
65
+ registry.set_override("unknown.flag", True, tenant_id="other")
66
+ message = Invalidation(channel=TENANT_DELETED_CHANNEL, key="deleted", origin="other")
67
+ await app.state.sm.invalidation.deliver(message.to_wire())
68
+ assert registry.tenant_override("unknown.flag", "deleted") is None
69
+ assert registry.tenant_override("unknown.flag", "other") is True