Implement governed distribution lists
This commit is contained in:
@@ -0,0 +1,309 @@
|
||||
import {
|
||||
apiFetch,
|
||||
apiPath,
|
||||
type ApiSettings
|
||||
} from "@govoplan/core-webui";
|
||||
|
||||
export type DefinitionKind = "static" | "parameterized" | "dynamic" | "template";
|
||||
export type EntryMode = "include" | "exclude" | "override";
|
||||
export type EntryKind =
|
||||
| "address_contact"
|
||||
| "address_list"
|
||||
| "address_email"
|
||||
| "raw_email"
|
||||
| "raw_postal_address"
|
||||
| "internal_mail"
|
||||
| "portal"
|
||||
| "idm_identity"
|
||||
| "idm_group"
|
||||
| "organization_unit"
|
||||
| "function"
|
||||
| "effective_function_incumbent"
|
||||
| "dataflow_result"
|
||||
| "distribution_list";
|
||||
export type DistributionChannel = "email" | "postal" | "internal_mail" | "portal";
|
||||
export type DistributionOutcome =
|
||||
| "usable"
|
||||
| "unresolved"
|
||||
| "invalid"
|
||||
| "suppressed"
|
||||
| "ambiguous"
|
||||
| "duplicate"
|
||||
| "policy_blocked"
|
||||
| "provider_unavailable"
|
||||
| "stale";
|
||||
|
||||
export type SourceReference = {
|
||||
provider: string;
|
||||
resource_type: string;
|
||||
resource_id: string;
|
||||
revision?: string | null;
|
||||
fingerprint?: string | null;
|
||||
label?: string | null;
|
||||
metadata: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type DistributionParameter = {
|
||||
key: string;
|
||||
value_type: "string" | "integer" | "number" | "boolean" | "date" | "datetime" | "string_list";
|
||||
label?: string | null;
|
||||
required: boolean;
|
||||
default?: unknown;
|
||||
allowed_values: unknown[];
|
||||
minimum?: number | null;
|
||||
maximum?: number | null;
|
||||
pattern?: string | null;
|
||||
description?: string | null;
|
||||
};
|
||||
|
||||
export type DistributionListEntryInput = {
|
||||
entry_key?: string | null;
|
||||
kind: EntryKind;
|
||||
mode: EntryMode;
|
||||
source: SourceReference;
|
||||
label?: string | null;
|
||||
purpose?: string | null;
|
||||
requested_channels: DistributionChannel[];
|
||||
effective_from?: string | null;
|
||||
effective_until?: string | null;
|
||||
configuration: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type DistributionListEntry = DistributionListEntryInput & {
|
||||
id: string;
|
||||
entry_key: string;
|
||||
order: number;
|
||||
};
|
||||
|
||||
export type DistributionListRevision = {
|
||||
id: string;
|
||||
revision: number;
|
||||
definition_kind: DefinitionKind;
|
||||
definition_hash: string;
|
||||
parameters: DistributionParameter[];
|
||||
constraints: Record<string, unknown>;
|
||||
source_fingerprints: Array<Record<string, unknown>>;
|
||||
entries: DistributionListEntry[];
|
||||
created_by_account_id?: string | null;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
export type DistributionList = {
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
scope_type: "tenant" | "group" | "user";
|
||||
scope_id?: string | null;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
status: string;
|
||||
current_revision: number;
|
||||
resource_revision: number;
|
||||
etag: string;
|
||||
metadata: Record<string, unknown>;
|
||||
deleted_at?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
revision: DistributionListRevision;
|
||||
};
|
||||
|
||||
export type DistributionListPayload = {
|
||||
name: string;
|
||||
description?: string | null;
|
||||
scope_type: "tenant" | "group" | "user";
|
||||
scope_id?: string | null;
|
||||
definition_kind: DefinitionKind;
|
||||
parameters: DistributionParameter[];
|
||||
constraints: Record<string, unknown>;
|
||||
entries: DistributionListEntryInput[];
|
||||
metadata: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type DistributionExplanation = {
|
||||
code: string;
|
||||
message: string;
|
||||
severity: "info" | "warning" | "error";
|
||||
provider?: string | null;
|
||||
source?: SourceReference | null;
|
||||
provenance: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type DistributionChannelCandidate = {
|
||||
channel: DistributionChannel;
|
||||
target: string;
|
||||
target_key: string;
|
||||
status: DistributionOutcome;
|
||||
contact_point_id?: string | null;
|
||||
locale?: string | null;
|
||||
preferred: boolean;
|
||||
reason_code?: string | null;
|
||||
explanation?: string | null;
|
||||
source?: SourceReference | null;
|
||||
decision_provenance: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type DistributionRecipient = {
|
||||
recipient_key: string;
|
||||
display_name: string;
|
||||
status: DistributionOutcome;
|
||||
channels: DistributionChannelCandidate[];
|
||||
identity_id?: string | null;
|
||||
account_id?: string | null;
|
||||
contact_id?: string | null;
|
||||
organization_unit_id?: string | null;
|
||||
function_id?: string | null;
|
||||
source_entry_ids: string[];
|
||||
explanations: DistributionExplanation[];
|
||||
attributes: Record<string, unknown>;
|
||||
provenance: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type ProviderEvidence = {
|
||||
provider: string;
|
||||
source: SourceReference;
|
||||
actual_revision?: string | null;
|
||||
actual_fingerprint?: string | null;
|
||||
stale: boolean;
|
||||
generated_at?: string | null;
|
||||
details: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type ExpansionResult = {
|
||||
list_id: string;
|
||||
revision_id: string;
|
||||
revision: number;
|
||||
definition_hash: string;
|
||||
recipients: DistributionRecipient[];
|
||||
excluded: DistributionRecipient[];
|
||||
diagnostics: DistributionExplanation[];
|
||||
provider_evidence: ProviderEvidence[];
|
||||
expansion_hash: string;
|
||||
generated_at: string;
|
||||
snapshot_id?: string | null;
|
||||
stale: boolean;
|
||||
truncated: boolean;
|
||||
};
|
||||
|
||||
export type ExpansionPayload = {
|
||||
revision?: number | null;
|
||||
effective_at?: string | null;
|
||||
purpose?: string | null;
|
||||
requested_channels?: DistributionChannel[];
|
||||
parameters?: Record<string, unknown>;
|
||||
preview?: boolean;
|
||||
freeze?: boolean;
|
||||
idempotency_key?: string | null;
|
||||
max_entries?: number;
|
||||
max_results?: number;
|
||||
max_depth?: number;
|
||||
max_provider_results?: number;
|
||||
};
|
||||
|
||||
export type DistributionSnapshot = {
|
||||
id: string;
|
||||
list_id: string;
|
||||
revision_id: string;
|
||||
revision: number;
|
||||
expansion_hash: string;
|
||||
effective_at: string;
|
||||
recipient_count: number;
|
||||
excluded_count: number;
|
||||
stale: boolean;
|
||||
truncated: boolean;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
export type ProviderOption = {
|
||||
key: string;
|
||||
kind: EntryKind;
|
||||
label: string;
|
||||
description?: string | null;
|
||||
provider: string;
|
||||
source: SourceReference;
|
||||
available: boolean;
|
||||
reason?: string | null;
|
||||
metadata: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type ProviderCatalogue = {
|
||||
items: ProviderOption[];
|
||||
unavailable_providers: DistributionExplanation[];
|
||||
};
|
||||
|
||||
export async function listDistributionLists(
|
||||
settings: ApiSettings,
|
||||
query = ""
|
||||
): Promise<DistributionList[]> {
|
||||
const result = await apiFetch<{ items: DistributionList[] }>(
|
||||
settings,
|
||||
apiPath("/api/v1/dist-lists/lists", { query, limit: 500 })
|
||||
);
|
||||
return result.items;
|
||||
}
|
||||
|
||||
export function createDistributionList(
|
||||
settings: ApiSettings,
|
||||
payload: DistributionListPayload
|
||||
): Promise<DistributionList> {
|
||||
return apiFetch(settings, "/api/v1/dist-lists/lists", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function updateDistributionList(
|
||||
settings: ApiSettings,
|
||||
item: DistributionList,
|
||||
payload: DistributionListPayload
|
||||
): Promise<DistributionList> {
|
||||
return apiFetch(settings, `/api/v1/dist-lists/lists/${encodeURIComponent(item.id)}`, {
|
||||
method: "PUT",
|
||||
headers: { "If-Match": item.etag },
|
||||
body: JSON.stringify({ ...payload, base_revision: item.resource_revision })
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteDistributionList(
|
||||
settings: ApiSettings,
|
||||
item: DistributionList
|
||||
): Promise<void> {
|
||||
return apiFetch(settings, `/api/v1/dist-lists/lists/${encodeURIComponent(item.id)}`, {
|
||||
method: "DELETE",
|
||||
headers: { "If-Match": item.etag },
|
||||
body: JSON.stringify({ base_revision: item.resource_revision })
|
||||
});
|
||||
}
|
||||
|
||||
export function expandDistributionList(
|
||||
settings: ApiSettings,
|
||||
listId: string,
|
||||
payload: ExpansionPayload
|
||||
): Promise<ExpansionResult> {
|
||||
return apiFetch(settings, `/api/v1/dist-lists/lists/${encodeURIComponent(listId)}/expand`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export async function listDistributionSnapshots(
|
||||
settings: ApiSettings,
|
||||
listId: string
|
||||
): Promise<DistributionSnapshot[]> {
|
||||
const result = await apiFetch<{ items: DistributionSnapshot[] }>(
|
||||
settings,
|
||||
`/api/v1/dist-lists/lists/${encodeURIComponent(listId)}/snapshots`
|
||||
);
|
||||
return result.items;
|
||||
}
|
||||
|
||||
export function listProviderOptions(
|
||||
settings: ApiSettings,
|
||||
query = "",
|
||||
limit = 50,
|
||||
signal?: AbortSignal
|
||||
): Promise<ProviderCatalogue> {
|
||||
return apiFetch(
|
||||
settings,
|
||||
apiPath("/api/v1/dist-lists/providers", { query, limit }),
|
||||
{ signal }
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user