feat(extensions): add a Storage API for extensions#39171
feat(extensions): add a Storage API for extensions#39171michael-s-molina wants to merge 13 commits intoapache:masterfrom
Conversation
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Codecov Report❌ Patch coverage is ❌ Your project check has failed because the head coverage (99.81%) is below the target coverage (100.00%). You can increase the head coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## master #39171 +/- ##
==========================================
+ Coverage 64.40% 64.42% +0.01%
==========================================
Files 2553 2568 +15
Lines 132558 133710 +1152
Branches 30746 30797 +51
==========================================
+ Hits 85377 86143 +766
- Misses 45695 46081 +386
Partials 1486 1486
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
e00efde to
95ec240
Compare
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
| /** | ||
| * Default TTL in seconds (1 hour). | ||
| */ | ||
| export const DEFAULT_TTL = 3600; |
There was a problem hiding this comment.
again, for better or for worse, but these have typically been defined in config.py to make them configurable. I would almost prefer to not have a default value at all, and make TTL a required parameter, than having this as a frontend defined fixed constant.
There was a problem hiding this comment.
Let's:
- Make
ttla required client parameter - Change
CACHE_DEFAULT_TIMEOUTtoMAX_TIMEOUT= 7 days to give admins more control.
Builds on top of Michael Molina's Tier 1+2 storage PR (#39171) to add database-backed persistent state for extensions. Backend: - ExtensionStorage model + migration (extension_storage table) - ExtensionStorageDAO with upsert/get/delete across user/shared scopes - PersistentStateImpl injected into superset_core.extensions.storage.persistent_state - ctx.storage.persistent property added to ExtensionStorage in context.py - REST endpoints GET/PUT/DELETE /api/v1/extensions/storage/persistent/<id>/<key> - Fernet encryption support via EXTENSION_STORAGE_ENCRYPTION_KEYS config - Use @transaction() decorator for DB write operations Frontend: - StorageTier declaration for persistent state in superset-core/src/storage - persistentState exported from storage module index - createPersistentState() factory in superset-frontend/src/core/storage - persistent tier wired into ExtensionContext alongside local/session/ephemeral - ExtensionStorage interface updated with persistent field Docs: - Added Tier 3 row to storage tier table - Added Tier 3: Persistent State section (matching Tier 2 format) - Frontend + backend + shared usage examples - Configuration section for EXTENSION_STORAGE_ENCRYPTION_KEYS Build/config: - Added python_version = "3.10" to mypy config (needed for match statement) - Added language_version: python3.10 to pre-commit mypy hook - Rebuilt superset-core lib to include storage module and persistentState Tests: - Unit tests for PersistentStateImpl covering get/set/remove, shared scope, and error context guards Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implement managed storage APIs for extensions with automatic namespace isolation. Storage is automatically bound to extensions before module execution, ensuring data privacy between extensions. - Tier 1 (localState/sessionState): Browser-based storage with user isolation - Tier 2 (ephemeralState): Server-side cache with TTL support - Update webpack externals to support subpath imports like @apache-superset/core/storage - Add storage documentation and update architecture docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
62203d4 to
93e3172
Compare
Summary
Superset Extensions need a stable, safe, and well-scoped API to persist data. Without a first-class storage API, extensions are forced to roll their own storage — writing directly to localStorage with ad-hoc keys on the client, or inserting rows into Superset's internal key_value table on the server — with no consistent namespacing, no isolation from Superset's own data, and no defined cleanup path on uninstall. This PR adds a first-class storage API that implements Tier 1, Tier 2, and Tier 3 of the Extensions Storage Design Proposal.
Changes
ctx.storage.local,ctx.storage.session) with user isolationctx.storage.ephemeral) with TTL support and Redis/Memcached compatibilityctx.storage.persistent) that survives server restarts and cache evictionsgetContext()which is bound to each extension via Module Federation@apache-superset/core/extensionsTier 1: Local State (Browser Storage)
Tier 2: Ephemeral State (Server Cache)
Tier 3: Persistent State (Database)
Database-backed storage that survives server restarts, cache evictions, and browser clears. Use for any data that must not be lost.
Shared State (Cross-User)
All storage operations are user-scoped by default. Use the
.sharedaccessor to read/write state visible to all users:API Endpoints (Tier 2 & Tier 3)
All endpoints follow the pattern
/api/v1/extensions/{publisher}/{name}/storage/{tier}/{key}. Append?shared=trueto any endpoint to operate on shared state visible to all users instead of the default user-scoped state./api/v1/extensions/{publisher}/{name}/storage/ephemeral/{key}/api/v1/extensions/{publisher}/{name}/storage/ephemeral/{key}/api/v1/extensions/{publisher}/{name}/storage/ephemeral/{key}/api/v1/extensions/{publisher}/{name}/storage/persistent/{key}/api/v1/extensions/{publisher}/{name}/storage/persistent/{key}/api/v1/extensions/{publisher}/{name}/storage/persistent/{key}Test plan
ctx.storage.localpersists data across page reloads in browser localStoragectx.storage.sessionclears when browser tab is closedctx.storage.ephemeralstores/retrieves data via the backend APIctx.storage.persistentstores/retrieves data and survives server restartsharedstorage is accessible across users🤖 Generated with Claude Code