3287ae399e
Constrain sensitive fields to row keys and preserve generic resource-definition contracts.
31 lines
1.7 KiB
TypeScript
31 lines
1.7 KiB
TypeScript
import type { ZodType } from 'zod'
|
|
import type { UiGridColumn } from '../ui/adapter/contracts'
|
|
export type CrudConcurrencyMode = 'NONE' | 'ETAG_IF_MATCH'
|
|
export type CrudIdempotencyMode = 'NONE' | 'IDEMPOTENCY_KEY'
|
|
export type CrudRowField<TRow> = Extract<keyof TRow, string>
|
|
export interface CrudResourceDefinition<TQuery, TRow, TForm> {
|
|
resourceId: string
|
|
querySchemaVersion: string
|
|
responseSchemaVersion: string
|
|
querySchema: ZodType<TQuery>
|
|
rowSchema: ZodType<TRow>
|
|
formSchema: ZodType<TForm>
|
|
columns: readonly UiGridColumn[]
|
|
sensitiveFields: readonly CrudRowField<TRow>[]
|
|
permissionPolicy: string
|
|
concurrencyMode: CrudConcurrencyMode
|
|
idempotencyMode: CrudIdempotencyMode
|
|
}
|
|
export function assertCrudResourceDefinition<TQuery, TRow, TForm>(definition: CrudResourceDefinition<TQuery, TRow, TForm>): void {
|
|
if (!definition.resourceId.trim()) throw new Error('resourceId is required')
|
|
if (!definition.querySchemaVersion.trim() || !definition.responseSchemaVersion.trim()) throw new Error('schema versions are required')
|
|
if (!definition.permissionPolicy.trim()) throw new Error('permissionPolicy is required')
|
|
if (definition.concurrencyMode !== 'NONE' && definition.concurrencyMode !== 'ETAG_IF_MATCH') throw new Error('invalid concurrencyMode')
|
|
if (definition.idempotencyMode !== 'NONE' && definition.idempotencyMode !== 'IDEMPOTENCY_KEY') throw new Error('invalid idempotencyMode')
|
|
const fields = new Set(definition.columns.map(x => x.field))
|
|
for (const sensitive of definition.sensitiveFields) {
|
|
const column = definition.columns.find(x => x.field === sensitive)
|
|
if (!fields.has(sensitive) || !column?.sensitive) throw new Error(`Sensitive field '${sensitive}' has no sensitive grid column contract`)
|
|
}
|
|
}
|