65 lines
2.2 KiB
TypeScript
65 lines
2.2 KiB
TypeScript
/**
|
|
* Capability-use example — Phase 06 #23.
|
|
*
|
|
* One handler that touches every "regular" capability so the audit-
|
|
* trail page shows a non-trivial set of rows per invocation:
|
|
* 1. storage.read to fetch the tenant's Slack webhook URL
|
|
* 2. http.outbound to POST a notification to Slack
|
|
* 3. log everywhere for structured tracing
|
|
*
|
|
* When run with the host audit recorder wired in, each capability call
|
|
* emits one PluginCapabilityUse row with outcome=success / error and a
|
|
* sha256(payloadDescriptor). Tenant admins see exactly which channel
|
|
* received which event without the message body ever being persisted.
|
|
*/
|
|
import { defineHook } from "../../src/index.js";
|
|
|
|
export interface EntityCreatedInput {
|
|
readonly tenantId: string;
|
|
readonly entityName: string;
|
|
readonly recordId: string;
|
|
}
|
|
|
|
export const handleEntityCreated = defineHook<EntityCreatedInput, { posted: boolean }>(
|
|
async (input, ctx) => {
|
|
ctx.log.info(
|
|
{ tenantId: input.tenantId, entity: input.entityName },
|
|
"capability-use.handleEntityCreated invoked"
|
|
);
|
|
|
|
if (!ctx.storage) {
|
|
ctx.log.warn({}, "storage capability not bound — manifest must declare storage.read");
|
|
return { posted: false };
|
|
}
|
|
const webhookUrl = await ctx.storage.get(`slack:${input.tenantId}:webhook-url`);
|
|
if (!webhookUrl) {
|
|
ctx.log.info({ tenantId: input.tenantId }, "no slack webhook configured — skipping");
|
|
return { posted: false };
|
|
}
|
|
|
|
if (!ctx.http) {
|
|
ctx.log.warn({}, "http capability not bound — manifest must declare http.outbound");
|
|
return { posted: false };
|
|
}
|
|
const body = JSON.stringify({
|
|
text: `New ${input.entityName} created (id ${input.recordId})`,
|
|
});
|
|
const response = await ctx.http.fetch({
|
|
url: webhookUrl,
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body,
|
|
});
|
|
|
|
if (response.status >= 200 && response.status < 300) {
|
|
ctx.log.info({ status: response.status }, "slack webhook posted");
|
|
return { posted: true };
|
|
}
|
|
ctx.log.error(
|
|
{ status: response.status, body: response.body.slice(0, 256) },
|
|
"slack webhook returned non-2xx"
|
|
);
|
|
return { posted: false };
|
|
}
|
|
);
|