Code-native integrations can call actions from Prismatic’s built-in (and custom) components without requiring a full low-code flow. You register the components you need in a componentRegistry, and then call their actions from onExecution via context.components.
Overview
Generate the component manifest
Run the Prismatic CLI to generate a TypeScript manifest for each component you want to use. The manifest describes every action, trigger, data source, and connection the component exposes.
Register the manifest
Pass the manifest to componentManifest() (for a single component) or componentManifests() (for multiple), and add it to componentRegistry on your integration.
Call actions in flows
Inside onExecution, call context.components.<componentKey>.<actionKey>({ ... }) to invoke a component action with strongly-typed inputs.
ComponentManifest type
A component manifest is the TypeScript description of everything a component can do.
interface ComponentManifest {
key: string;
public: boolean;
signature: string | null;
actions: Record<string, ComponentManifestAction>;
triggers: Record<string, ComponentManifestTrigger>;
dataSources: Record<string, ComponentManifestDataSource>;
connections: Record<string, ComponentManifestConnection>;
}
Each action follows this shape:
interface ComponentManifestAction {
key?: string;
perform: (values: any) => Promise<unknown>;
inputs: Record<string, { inputType: InputFieldType; collection?: CollectionType; required?: boolean; default?: unknown }>;
examplePayload?: unknown;
}
You typically do not write manifests by hand — they are generated by the Prismatic CLI from the component’s published source.
componentManifest()
Wraps a single component manifest object with type validation.
import { componentManifest } from "@prismatic-io/spectral";
import slackManifest from "./componentManifests/slack";
const slack = componentManifest(slackManifest);
componentManifests()
Wraps a record of multiple manifests at once. The record keys become the keys used to access each component in context.components.
import { componentManifests } from "@prismatic-io/spectral";
import slackManifest from "./componentManifests/slack";
import httpManifest from "./componentManifests/http";
export const myComponents = componentManifests({
slack: slackManifest,
http: httpManifest,
});
Adding components to an integration
Pass your component registry to the componentRegistry field of integration().
import { integration, flow, componentManifests } from "@prismatic-io/spectral";
import slackManifest from "./componentManifests/slack";
import httpManifest from "./componentManifests/http";
const registry = componentManifests({
slack: slackManifest,
http: httpManifest,
});
export default integration({
name: "Notify on Event",
componentRegistry: registry,
flows: [
flow({
name: "Send Slack Notification",
stableKey: "send-slack-notification",
onExecution: async (context, params) => {
const payload = params.onTrigger.results.body.data as { message: string };
// Call the Slack component's 'postMessage' action
const result = await context.components.slack.postMessage({
channel: { value: "#alerts" },
message: { value: payload.message },
});
return { data: result };
},
}),
],
});
The keys in the componentManifests() record (slack, http above) must match how you reference them in context.components. Choose names that are consistent and descriptive.
Using component connections
Component connections are referenced from a config page and then passed as inputs when calling a component action.
import {
integration,
flow,
configPage,
connectionConfigVar,
componentManifests,
} from "@prismatic-io/spectral";
import slackManifest from "./componentManifests/slack";
const registry = componentManifests({ slack: slackManifest });
export default integration({
name: "Slack Notifier",
componentRegistry: registry,
configPages: {
"Slack Settings": configPage({
elements: {
// Reference the connection from the Slack component's manifest
"Slack Connection": {
stableKey: "slack-connection",
dataType: "connection",
connection: {
component: "slack",
key: "oauth2",
},
},
},
}),
},
flows: [
flow({
name: "Alert Flow",
stableKey: "alert-flow",
onExecution: async (context, params) => {
const slackConn = context.configVars["Slack Connection"];
await context.components.slack.postMessage({
connection: { configVar: "Slack Connection" },
channel: { value: "#ops-alerts" },
message: { value: "Deployment complete" },
});
return { data: null };
},
}),
],
});
Testing with createMockContextComponents()
The Spectral testing module exports createMockContextComponents() to provide mock implementations of component actions in unit tests. This lets you test your flow logic without making real API calls.
import { testing } from "@prismatic-io/spectral";
import { processOrderFlow } from "../src/flows/processOrder";
const { createMockContextComponents, createMockContext } = testing;
describe("processOrderFlow", () => {
it("calls the HTTP component and returns fulfilled status", async () => {
const mockComponents = createMockContextComponents({
http: {
post: jest.fn().mockResolvedValue({
data: { id: "order-99", status: "fulfilled" },
}),
},
});
const context = createMockContext({
configVars: {
"API Base URL": "https://api.example.com",
},
components: mockComponents,
});
const result = await processOrderFlow.onExecution(context, {
onTrigger: {
results: {
body: { data: { orderId: "ORD-1" } },
headers: {},
queryParameters: {},
rawBody: { data: null },
pathFragment: "",
webhookUrls: {},
webhookApiKeys: {},
invokeUrl: "",
executionId: "test",
customer: { id: "", name: "", externalId: "" },
instance: { id: "", name: "" },
user: { id: "", email: "", externalId: "", name: "" },
integration: { id: "", name: "", versionSequenceId: "", externalVersion: "", versionComment: "" },
flow: { id: "", name: "" },
startedAt: new Date().toISOString(),
globalDebug: false,
},
},
});
expect(result.data).toMatchObject({ fulfilled: true });
expect(mockComponents.http.post).toHaveBeenCalledWith(
expect.objectContaining({ url: { value: expect.stringContaining("example.com") } }),
);
});
});
Component registry type augmentation
For full TypeScript inference of context.components, augment IntegrationDefinitionComponentRegistry in a declaration file:
// src/types.d.ts
import type { IntegrationDefinitionComponentRegistry } from "@prismatic-io/spectral";
import type slackManifest from "./componentManifests/slack";
import type httpManifest from "./componentManifests/http";
declare module "@prismatic-io/spectral" {
interface IntegrationDefinitionComponentRegistry {
slack: typeof slackManifest;
http: typeof httpManifest;
}
}
With this in place, context.components.slack.postMessage is fully typed, including its input fields.
You only need to augment IntegrationDefinitionComponentRegistry if you want compile-time type checking for component action inputs. Without it, context.components is typed as Record<string, ComponentManifest>.