186 lines
5.5 KiB
TypeScript
186 lines
5.5 KiB
TypeScript
import { apiFetch, apiPath, type ApiSettings } from "@govoplan/core-webui";
|
|
|
|
export type ViewPolicyScope = "system" | "tenant" | "group" | "user";
|
|
|
|
export type ViewPolicyItem = {
|
|
allow_view?: boolean;
|
|
allow_select?: boolean;
|
|
allow_assign?: boolean;
|
|
allow_edit?: boolean;
|
|
allow_derive?: boolean;
|
|
allow_workflow_activate?: boolean;
|
|
allowed_view_ids?: string[];
|
|
visible_surface_ids?: string[];
|
|
};
|
|
|
|
export type EffectiveViewPolicy = {
|
|
allow_view: boolean;
|
|
allow_select: boolean;
|
|
allow_assign: boolean;
|
|
allow_edit: boolean;
|
|
allow_derive: boolean;
|
|
allow_workflow_activate: boolean;
|
|
allowed_view_ids?: string[] | null;
|
|
visible_surface_ids?: string[] | null;
|
|
};
|
|
|
|
export type ViewPolicyScopeResponse = {
|
|
scope_type: ViewPolicyScope;
|
|
scope_id?: string | null;
|
|
id?: string | null;
|
|
revision?: number | null;
|
|
policy: ViewPolicyItem;
|
|
effective_policy: EffectiveViewPolicy;
|
|
parent_policy: EffectiveViewPolicy;
|
|
source_path: Array<{
|
|
scope_type: string;
|
|
scope_id?: string | null;
|
|
source_id?: string | null;
|
|
fields?: string[];
|
|
}>;
|
|
diagnostics: Array<Record<string, unknown>>;
|
|
};
|
|
|
|
export type ViewPolicyReferenceData = {
|
|
views: Array<{ id: string; name: string; scope_type?: string }>;
|
|
surfaces: Array<{ id: string; label: string; module_id: string; kind: string }>;
|
|
};
|
|
|
|
export type PolicyImpactCategory = "newly_allowed" | "newly_denied" | "unchanged" | "indeterminate";
|
|
|
|
export type PolicyImpactPreviewResponse = {
|
|
preview_id: string;
|
|
proposal_hash: string;
|
|
policy_family: string;
|
|
scope_type: string;
|
|
scope_id?: string | null;
|
|
base_revision?: number | null;
|
|
counts: Record<PolicyImpactCategory, number>;
|
|
effects: Array<{
|
|
category: PolicyImpactCategory;
|
|
subject: {
|
|
module_id: string;
|
|
resource_type: string;
|
|
resource_id: string;
|
|
action: string;
|
|
label?: string | null;
|
|
scope_type?: string | null;
|
|
scope_id?: string | null;
|
|
};
|
|
current_allowed?: boolean | null;
|
|
proposed_allowed?: boolean | null;
|
|
rule: string;
|
|
current_sources: Array<{ path: string; label: string }>;
|
|
proposed_sources: Array<{ path: string; label: string }>;
|
|
explanation?: string | null;
|
|
}>;
|
|
populations: Array<{
|
|
provider_id: string;
|
|
state: "complete" | "sampled" | "truncated" | "unavailable";
|
|
returned: number;
|
|
total_available?: number | null;
|
|
explanation?: string | null;
|
|
}>;
|
|
details_hidden: boolean;
|
|
details_explanation?: string | null;
|
|
high_impact: boolean;
|
|
};
|
|
|
|
export function fetchViewPolicy(
|
|
settings: ApiSettings,
|
|
scope: ViewPolicyScope,
|
|
scopeId?: string | null
|
|
): Promise<ViewPolicyScopeResponse> {
|
|
return apiFetch(settings, apiPath(`/api/v1/admin/view-policies/${scope}`, {
|
|
scope_id: scopeId || undefined
|
|
}));
|
|
}
|
|
|
|
export function updateViewPolicy(
|
|
settings: ApiSettings,
|
|
scope: ViewPolicyScope,
|
|
scopeId: string | null | undefined,
|
|
policy: ViewPolicyItem,
|
|
impactPreview?: Pick<PolicyImpactPreviewResponse, "preview_id" | "proposal_hash"> | null
|
|
): Promise<ViewPolicyScopeResponse> {
|
|
return apiFetch(settings, apiPath(`/api/v1/admin/view-policies/${scope}`, {
|
|
scope_id: scopeId || undefined
|
|
}), {
|
|
method: "PUT",
|
|
body: JSON.stringify({
|
|
policy,
|
|
impact_preview_id: impactPreview?.preview_id,
|
|
impact_proposal_hash: impactPreview?.proposal_hash
|
|
})
|
|
});
|
|
}
|
|
|
|
export function deleteViewPolicy(
|
|
settings: ApiSettings,
|
|
scope: ViewPolicyScope,
|
|
scopeId?: string | null,
|
|
impactPreview?: Pick<PolicyImpactPreviewResponse, "preview_id" | "proposal_hash"> | null
|
|
): Promise<ViewPolicyScopeResponse> {
|
|
return apiFetch(settings, apiPath(`/api/v1/admin/view-policies/${scope}`, {
|
|
scope_id: scopeId || undefined,
|
|
impact_preview_id: impactPreview?.preview_id,
|
|
impact_proposal_hash: impactPreview?.proposal_hash
|
|
}), { method: "DELETE" });
|
|
}
|
|
|
|
export function previewViewPolicyImpact(
|
|
settings: ApiSettings,
|
|
scope: ViewPolicyScope,
|
|
scopeId: string | null | undefined,
|
|
policy: ViewPolicyItem,
|
|
population: { viewIds: string[]; surfaceIds: string[] }
|
|
): Promise<PolicyImpactPreviewResponse> {
|
|
return apiFetch(settings, "/api/v1/admin/policy-impact/preview", {
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
policy_family: "view",
|
|
scope_type: scope,
|
|
scope_id: scopeId || null,
|
|
proposed_policy: policy,
|
|
populations: [
|
|
{
|
|
provider_id: "views",
|
|
selector: {
|
|
include_views: true,
|
|
include_surfaces: false,
|
|
view_ids: population.viewIds.slice(0, 500)
|
|
},
|
|
limit: 500
|
|
},
|
|
{
|
|
provider_id: "views",
|
|
selector: {
|
|
include_views: false,
|
|
include_surfaces: true,
|
|
surface_ids: population.surfaceIds.slice(0, 500)
|
|
},
|
|
limit: 500
|
|
}
|
|
],
|
|
include_details: true
|
|
})
|
|
});
|
|
}
|
|
|
|
export async function fetchViewPolicyReferences(settings: ApiSettings): Promise<ViewPolicyReferenceData> {
|
|
const [definitionResult, surfaceResult] = await Promise.allSettled([
|
|
apiFetch<{ definitions: Array<{ id: string; name: string; scope_type?: string }> }>(
|
|
settings,
|
|
apiPath("/api/v1/views/definitions", { scope_type: "tenant", include_inherited: true })
|
|
),
|
|
apiFetch<{ surfaces: Array<{ id: string; label: string; module_id: string; kind: string }> }>(
|
|
settings,
|
|
"/api/v1/views/surfaces"
|
|
)
|
|
]);
|
|
return {
|
|
views: definitionResult.status === "fulfilled" ? definitionResult.value.definitions : [],
|
|
surfaces: surfaceResult.status === "fulfilled" ? surfaceResult.value.surfaces : []
|
|
};
|
|
}
|