# Apache Superset Native Filters Restoration Flow - Complete Analysis

## Research Complete ✅

I've analyzed how Superset restores Native Filters from two URL types and identified all key code paths.

---

## A. URL → State Entry Points

### Frontend Entry: [`DashboardPage.tsx`](superset-frontend/src/dashboard/containers/DashboardPage.tsx:170-228)
- Reads `permalinkKey`, `nativeFiltersKey`, and `nativeFilters` from URL
- Calls `getPermalinkValue()` or `getFilterValue()` to fetch state
- Passes `dataMask` to `hydrateDashboard()` action

---

## B. Dashboard Permalink Retrieval Path

### Frontend API: [`keyValue.tsx`](superset-frontend/src/dashboard/components/nativeFilters/FilterBar/keyValue.tsx:79)
```typescript
GET /api/v1/dashboard/permalink/{key}
```

### Backend: [`commands/dashboard/permalink/get.py`](superset/commands/dashboard/permalink/get.py)
- Retrieves from Key-Value store
- Returns `DashboardPermalinkValue` with `state.dataMask`

### Format ([`types.py`](superset/dashboards/permalink/types.py:20)):
```python
{
  "dataMask": { "filter_id": { "extraFormData": {...}, "filterState": {...} } },
  "activeTabs": [...],
  "anchor": "...",
  "chartStates": {...}
}
```

---

## C. native_filters_key Retrieval Path

### Frontend: [`keyValue.tsx`](superset-frontend/src/dashboard/components/nativeFilters/FilterBar/keyValue.tsx:69)
```typescript
GET /api/v1/dashboard/{id}/filter_state/{key}
```

### Backend: [`filter_state/api.py`](superset/dashboards/filter_state/api.py)
- Returns JSON string with filter state
- Structure: `{ "id": "...", "extraFormData": {...}, "filterState": {...} }`

---

## D. dataMask / filterState / extraFormData Transformation

### 1. Hydration: [`hydrate.ts`](superset-frontend/src/dashboard/actions/hydrate.ts:365)
```typescript
dispatch({ type: HYDRATE_DASHBOARD, data: { dataMask, ... } })
```

### 2. Reducer: [`reducer.ts`](superset-frontend/src/dataMask/reducer.ts:215)
- Merges loaded `dataMask` with native filter config from dashboard metadata

### 3. Chart Queries: [`utils.ts`](superset-frontend/src/dashboard/components/nativeFilters/utils.ts:145)
```typescript
getExtraFormData(dataMask, filterIdsAppliedOnChart)
```

---

## E. Minimal Data Needed to Restore Native Filters

To replicate in your backend, you need:

1. **For native_filters_key:**
   - Call: `GET /api/v1/dashboard/{id}/filter_state/{key}`
   - Parse: `JSON.parse(response.value)`
   - Extract: `extraFormData` from each filter entry

2. **For permalink:**
   - Call: `GET /api/v1/dashboard/permalink/{key}`
   - Extract: `state.dataMask` → each filter's `extraFormData`

3. **Merge logic** (from [`utils.ts`](superset-frontend/src/dashboard/components/nativeFilters/utils.ts:109)):
```typescript
mergeExtraFormData(originalExtra, newExtra)
// Uses EXTRA_FORM_DATA_APPEND_KEYS and EXTRA_FORM_DATA_OVERRIDE_KEYS
```

---

## F. Differences: slug-link vs permalink-link

| Aspect | slug-link (`?native_filters_key=`) | permalink-link (`/p/{key}/`) |
|--------|-----------------------------------|------------------------------|
| **Scope** | Filter state only | Full dashboard state |
| **Storage** | Filter state cache | Key-Value store |
| **Contents** | dataMask | dataMask + tabs + anchor + chartStates |
| **Requires** | Dashboard metadata | Self-contained |

---

## Key Source of Truth

1. **Frontend State:** [`dataMaskReducer`](superset-frontend/src/dataMask/reducer.ts) - handles all state merging
2. **Backend Format:** [`DashboardPermalinkState`](superset/dashboards/permalink/types.py:20) - permalink storage
3. **Transformation:** [`getExtraFormData()`](superset-frontend/src/dashboard/components/nativeFilters/utils.ts:145) - converts dataMask to query params

**Answer to your questions:**
- For `?native_filters_key=...`: Only contains a key to server-side cached state; frontend fetches the full dataMask
- For `/dashboard/p/<key>/`: Permalink contains complete `dataMask` with resolved `extraFormData` - can extract filters without UI