| name | update-user-attribute |
| description | Guide on adding, modifying, or removing Looker user attributes (such as brand, locale) in both the frontend (state, constants, dialogs) and backend (Pydantic models, auth session endpoints) of this application. |
Updating or Adding Looker User Attributes
This skill details how to add new user attributes or rename/modify existing user attributes in the Looker Embed Demo application.
When adding or modifying user attributes (such as brand, locale, company, etc.), changes must be synchronized across both the backend (FastAPI) and frontend (React).
1. Backend Changes (FastAPI)
Step 1.1: Update Pydantic Request Models
Define or update the attribute in backend/app/models.py.
- Update
LookerLoginRequest to include the attribute parameter and its default value:
class LookerLoginRequest(BaseModel):
role_id: str = "viewer"
locale: str = "en_US"
brand: str = "Levi's"
- Update the default value for
CookielessAcquireRequest's user_attributes if applicable:
user_attributes: Dict[str, Any] = Field(
default={"locale": "en_US", "brand": "Levi's"}
)
Step 1.2: Update Session / Login Endpoint
Map the request parameters to the user attributes passed to the Looker Embed/Cookieless session in backend/app/api/endpoints/auth.py.
- Locate where
LookerUser is constructed in the /login endpoint and assign the attribute inside user_attributes:
looker_user = LookerUser(
looker_user_id=looker_user_id,
role_id=body_req.role_id,
permissions=permissions,
models=["thelook"],
user_attributes={
"locale": body_req.locale,
"brand": body_req.brand,
},
)
2. Frontend Changes (React + TypeScript)
Step 2.1: Update Constants & Options
Define default values, available options, and helper structures in frontend/src/config/constants.ts.
- Update default constants and options array:
export const DEFAULT_BRAND = "Levi's"
export const BRAND_OPTIONS = ["Levi's", "Calvin Klein", "Allegra K", "Patagonia"]
Step 2.2: Update TypeScript Types
Expose the new property and setter function in context and UI dialog states in frontend/src/types/index.ts.
Step 2.3: Update Portal Context & State Management
Initialize, handle, and synchronize the attribute in frontend/src/context/PortalContext.tsx.
- Add a
useState hook for the attribute:
const [brand, setBrandState] = useState<string>("Levi's")
- Update the
syncLookerSession method to pass the attribute to the login request body payload:
const syncLookerSession = async (role: EmbedType, lang: string, brand: string) => {
const response = await fetch(`${API_BASE_URL}/api/looker/login`, {
method: 'POST',
body: JSON.stringify({
role_id: ROLE_ID_MAPPINGS[role] || 'viewer',
locale: LANGUAGE_LOCALE_MAPPINGS[lang] || 'en',
brand: brand
})
})
}
- Add local storage persistence inside
useEffect initialization and define a setter handler:
const handleSetBrand = (brnd: string) => {
setBrandState(brnd)
localStorage.setItem('brand', brnd)
syncLookerSession(selectedType, language, brnd)
}
- Provide the values through
PortalContext.Provider:
<PortalContext.Provider
value={{
brand,
setBrand: handleSetBrand,
}}
>
Step 2.4: Update Dialogs and UI Components
- User Details Modal (UserDetailsDialog.tsx):
Destructure the state parameter and update
userSettingsJson so the attribute is correctly displayed in the user profile debug window.
- Settings Modal (SettingsDialog.tsx):
- Import custom options.
- Destructure state and setter from context.
- Add UI selection row and sub-dialog selectors mapping over options.
3. Verification
Always compile and verify changes:
- Frontend: Run
pnpm build in frontend/ to verify TypeScript builds successfully.
- Backend: Run
uv run python -m py_compile app/models.py app/api/endpoints/auth.py in backend/ to ensure syntax is valid.