wow-view-store reference
Released with Wow 9.2.0
@ahoo-wang/wow-view-store is released with Wow 9.2.0, together with wow-view-engine, from the same tag and with the same version; it is not on npm before that. A patch release never breaks its public surface (the exports of the entry and the error codes); a minor release may, and its release notes list every break with the steps to follow (version ranges). The package README and the design are the source of truth.
WowViewStore implements the view engine's ViewStore port on the view store: saved views and preferences as two Wow aggregates, served by the standalone wow-view-store-server or by a Wow service that embeds wow-view-store-starter.
Entry
| Export | Role |
|---|---|
WowViewStore | The store: new WowViewStore({ fetcher, permissions? }), handed to new ViewEngine({ store }) |
WowViewStoreOptions | fetcher: the fetcher whose interceptors carry the tenant, the owner and the application; permissions: which buttons are enabled |
SHARED_OWNER_ID | (shared), the owner segment of shared views and shared preferences |
WowViewStoreErrorCodes | The view store's own error codes, beside Wow's |
export interface WowViewStoreOptions {
fetcher: Fetcher;
permissions?: (definitionId: string) => ViewPermissions;
}Contract
| Rule | Behavior |
|---|---|
| Who is asking | The store takes no tenant, user or application. The fetcher's interceptors fill the path's {tenantId} and, on a personal path, {ownerId} (fetcher-cosec's ResourceAttributionRequestInterceptor, from the token) and send CoSec-App-Id; a host nobody signs in to fills defaults with an interceptor of its own |
| Audience | The owner segment of the path: a personal view on the caller's path, a shared one on owner/(shared). "Make shared" is share on the view's path, "Make personal" is claim on the caller's own path |
| Writes | Command-Request-Id is the port's requestId, Command-Aggregate-Version its revision; each waits for the snapshot and answers the view read back |
| Retries | A write refused as a stale version or a repeated request id is looked up by its request id first, and a retry answers what its first attempt wrote. The server does not deduplicate a create (it generates the id); a store asks the replay route before posting a retry of its own create again, so only a retry from another store makes a second view |
| Errors | By Wow's error code onto CONFLICT, NOT_FOUND, FORBIDDEN, INVALID and UNAVAILABLE; the HTTP status only when no known code came back; UNSUPPORTED from a server with no view store. The error keeps the server's code as detail.code, and an UNAVAILABLE the server answered says reachable |
| List order | System views, then shared, then personal, each audience oldest first; at most 1,000 of each audience |
| Shared boards | A view a shared dashboard shows stays shared: the claim is INVALID, its boards the boards' titles as stored, which the view engine says in its own words |
Hosts
The example server and the compensation service embed wow-view-store-starter; the compensation console keeps its views there. Nobody signs in to the console, so it adds a request interceptor of its own that fills the tenant (0), the owner (shared) and its application (compensation-dashboard) where a request names none: every view and preference it keeps is shared, and its permissions turn personal views off. A host that embeds the starter gives the view store's Kafka topics a prefix of their own with wow.view-store.kafka.topic-prefix, which leaves the host's own topics as they are.
CoSec gateway rules
The server does not authenticate: it takes the tenant, the owner and the application as the request names them, so every deployment runs behind the CoSec gateway, whose path rules tie each to the token:
| Path | Allow when |
|---|---|
/view-store/tenant/{tenantId}/owner/{ownerId}/**, except …/view/{id}/claim and …/view/{id}/share | {tenantId} is the token's tenant and {ownerId} is its sub |
/view-store/tenant/{tenantId}/owner/(shared)/**, reads | {tenantId} is the token's tenant |
/view-store/tenant/{tenantId}/owner/(shared)/**, writes | the token's tenant, and the role that may write shared views |
PUT /view-store/tenant/{tenantId}/owner/{ownerId}/view/{id}/claim, …/share | the token's tenant, {ownerId} is its sub, and the role that may write shared views |
A claim takes a shared view out of everyone's list and a share publishes a personal one into it, so the personal rule must not admit either on its own: leave …/view/{id}/claim and …/view/{id}/share out of that rule, and only the claim-and-share rule decides (otherwise anyone could publish a shared view by creating a personal one and sharing it). CoSec-App-Id is authenticated by CoSec and keeps applications apart; a host gives permissions by the same role the gateway checks: createShared and changeAudience need the shared-write role. The full rules, with the reasons, are in the view store README.