Invitations accept: use updated typed OpenAPI; keep router guard tests green

This commit is contained in:
jableader 2025-11-01 19:21:12 +11:00
parent 6ec15debef
commit ddad070eb7
3 changed files with 27 additions and 30 deletions

View file

@ -153,7 +153,7 @@ Progress Log (Nov 1, 2025)
- Added auth API tests driving a minimal multitenant-ready surface.
- Implemented `loginWithPassword`, `logout`, and stubs in `src/api/auth.ts` to satisfy tests.
- Refactored `useAuth` to add households and activeHousehold state, plus `loginWithPassword` and `logout`. Tests added and passing.
- Added router tests and implemented feature-flagged nested routes and new public routes. Placeholders for onboarding/invitations added.
- Added router tests and implemented slug-only nested routes and new public routes. Placeholders for onboarding/invitations added.
- Implemented `useHousehold.ts`, Authorization provider in API client, minimal `HouseholdSwitcher.vue`, and mounted it. Replaced header injection test with path-scoped assertion.
- Implemented JWT login/register in `auth.ts` and wired token to client provider. `useAuth` updated with households fetching. `currentUser` refactored to token-only refresh plus households load.
- Router guard updated to handle public/multitenant routing and redirects.
@ -166,7 +166,7 @@ Progress Log (Nov 1, 2025)
- Backend updated OpenAPI and codegen has been run:
- Many endpoints are now path-scoped with `{householdSlug}` (recipes, meals, shopping, invitations (create), whoami).
- Auth endpoints (login/register/refresh/logout) are fully typed; `refresh` returns only `{ accessToken, tokenType }`.
- Completed migration to typed path parameters; header injection removed; only small raw fetch helpers remain for endpoints not yet in OpenAPI (persons, parse, members list).
- Completed migration to typed path parameters; header injection removed; only small raw fetch helpers remain for endpoints not yet in OpenAPI (persons, parse).
---

View file

@ -1,33 +1,16 @@
import { api, fetchApi } from '@/api/client'
import { api } from '@/api/client'
export type Household = { id: number; name: string; slug: string }
function hasKey<T extends object, K extends PropertyKey>(obj: T, key: K): obj is T & Record<K, unknown> {
return Object.prototype.hasOwnProperty.call(obj, key)
}
function isHousehold(value: unknown): value is Household {
if (typeof value !== 'object' || value === null) return false
const v = value as Record<string, unknown>
return (
typeof v.id === 'number' &&
typeof v.name === 'string' &&
typeof v.slug === 'string'
)
}
export async function acceptInvitation(token: string): Promise<Household> {
// OpenAPI requestBody type currently rejects fields; use raw fetch until spec is corrected.
const resp = await fetchApi('/api/v1/invitations/accept', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ token }),
})
if (!resp.ok) throw new Error(`${resp.status} ${resp.statusText || 'HTTP error'}`)
const obj: unknown = await resp.json().then((x: unknown) => x)
const wrapped = obj && typeof obj === 'object' ? (obj as Record<string, unknown>) : null
const h = wrapped && hasKey(wrapped, 'household') ? wrapped.household : undefined
if (!isHousehold(h)) throw new Error('Invalid invitation accept response')
const { data, response, error } = await api.POST('/api/v1/invitations/accept', { body: { token } })
if (!response.ok) {
throw new Error(`${response.status} ${response.statusText || 'HTTP error'}${error ? `: ${String(error)}` : ''}`)
}
const h = data?.household
if (!h || typeof h.id !== 'number' || typeof h.name !== 'string' || typeof h.slug !== 'string') {
throw new Error('Invalid invitation accept response')
}
return { id: h.id, name: h.name, slug: h.slug }
}

View file

@ -403,6 +403,20 @@ export interface paths {
export type webhooks = Record<string, never>;
export interface components {
schemas: {
/** AcceptInvitationBody */
AcceptInvitationBody: {
/** Token */
token: string;
};
/** AcceptInvitationResponse */
AcceptInvitationResponse: {
/**
* Status
* @default accepted
*/
status: string;
household: components["schemas"]["HouseholdResponse"];
};
/** CreateHouseholdBody */
CreateHouseholdBody: {
/** Name */
@ -1187,7 +1201,7 @@ export interface operations {
};
requestBody: {
content: {
"application/json": Record<string, never>;
"application/json": components["schemas"]["AcceptInvitationBody"];
};
};
responses: {
@ -1197,7 +1211,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"application/json": unknown;
"application/json": components["schemas"]["AcceptInvitationResponse"];
};
};
/** @description Validation Error */