# pp_frontend

One CSV per frontend repo. Each row is a `(resource_type, action)` pair the frontend asks PP about — typically used to gate UI rendering (show/hide buttons, conditional routes).

All files use the same six columns: `microservice, endpoint, resource_type, action, pp_authorization_method, source_file`. The `endpoint` column for FE rows is the literal call-site `<file>:<line>` (FE gates have no HTTP method/path of their own).

## Coverage

| Repo                              | Rows | Pattern                                              | Underlying call                                                              |
|-----------------------------------|------|------------------------------------------------------|------------------------------------------------------------------------------|
| `frontend-audience-development`   |   14 | Declarative `RESOURCE_TYPE_ACTIONS` table at `src/permissions/constants.ts` | Bulk `Identity.isAuthorizedForTenants(resourceTypeActions)` at app load → server-side `get_authorized_tenants`. |
| `frontend-content`                |   13 | Per-component `useCanPerformAction` + `useCrudPermissions` wrapper | `Identity.canPerform({resourceType, action})` → server-side `can_perform`.   |
| `frontend-royalties`              |    3 | Per-component `useCanPerformAction` (direct)         | `Identity.canPerform({resourceType, action})` → server-side `can_perform`.   |
| **Total**                         | **30** |                                                   |                                                                              |

## Column reference

All CSVs in this dir share the same six columns:

| Column                    | Meaning                                                                                                                                                                                                                                                |
|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `microservice`            | Frontend repo slug (e.g. `frontend-content`). Reused as `microservice` even though the frontend is the *consumer* of the check, not the service that authoritatively gates it. The actual server-side resolver is in `graphql-user`.                  |
| `endpoint`                | `<repo-relative source path>:<line>` of the `useCanPerformAction` (or wrapper) call site. FE permission gates have no HTTP method/path of their own. For the `frontend-audience-development` declarative table, this points at the entry in `RESOURCE_TYPE_ACTIONS`. |
| `resource_type`           | The cerbos resource type checked. The 3rd argument to `useCanPerformAction` (or the `resourceType` field in the declarative table). Constants like `PERMISSIONS_RESOURCE_TYPES.BANK_INFO` are resolved before emit (→ `bank_info`).                    |
| `action`                  | The cerbos action checked. The 2nd argument to `useCanPerformAction`. Constants are resolved before emit.                                                                                                                                              |
| `pp_authorization_method` | `can_perform` for `useCanPerformAction` rows (resolves to `Identity.canPerform` → `checkAuthenticatedUserResourceTypeActions`). `get_authorized_tenants` for `frontend-audience-development`'s bulk app-load fetch (resolves to `Identity.isAuthorizedForTenants`). |
| `source_file`             | Often empty for frontend rows because `endpoint` already encodes the file:line. Populated only when the call goes through a wrapper hook and we want to record both the wrapper file and the consumer.                                                |

## Patterns observed

Three distinct FE patterns, in order of when they were adopted:

1. **Declarative table + bulk fetch** (`frontend-audience-development`): one centralized `RESOURCE_TYPE_ACTIONS` array, fetched once at app load and cached. Lowest runtime overhead, highest discoverability — the table itself is the inventory.
2. **Per-component `useCanPerformAction` hook** (`frontend-royalties`): each component calls the hook with its own `(resourceType, action)`. Easy to author per-feature; harder to audit across the app.
3. **Wrapper around the hook** (`frontend-content`'s `useCrudPermissions`): instantiates the underlying hook 4 times (`view`, `create`, `edit`, `delete`) for one resourceType, regardless of which check the consumer reads. Convenient for CRUD pages but **issues PP requests for actions the page may not actually use** — worth keeping in mind for request-volume planning.

## Underlying server call

| FE pattern                               | Resolves via                                     | `pp_authorization_method` |
|------------------------------------------|--------------------------------------------------|---------------------------|
| `useCanPerformAction(id, action, type)`  | `Identity.canPerform` resolver in `graphql-user/src/resolvers/identity.ts:319` | `can_perform`             |
| Bulk app-load (`isAuthorizedForTenants`) | `Identity.isAuthorizedForTenants` resolver in `graphql-user/src/resolvers/identity.ts:66`  | `get_authorized_tenants`  |

## Notes

- **`view:*` wildcard** appears in `frontend-content`'s neighbouringRights wrapper — deliberate: `policies/neighbouring_rights/contribution.yml:17` declares the rule with `actions: ["view:*"]`. The two literal strings match.
- **Constants split-brain in frontend-content:** `PERMISSIONS_ACTIONS.VIEW` is defined as `view:*` in the constants file, but the ownershipRights wrapper uses the literal `'view'` (not the constant). The two modules disagree on the action string for the view tier — likely an in-progress migration.
