feat: authorize workflow contract authoring
This commit is contained in:
@@ -157,6 +157,55 @@ const DraftWorkspaceSchema = Schema.Struct({
|
||||
workspace_id: Schema.String,
|
||||
});
|
||||
|
||||
const AuthoringPathOptionSchema = Schema.Struct({
|
||||
path: Schema.String,
|
||||
label: Schema.String,
|
||||
origin: Schema.Literal(
|
||||
"workflow_input",
|
||||
"workflow_state",
|
||||
"runtime_context",
|
||||
"step_input",
|
||||
"step_output",
|
||||
"workflow_output",
|
||||
),
|
||||
schema: JsonObjectSchema,
|
||||
required: Schema.Boolean,
|
||||
availability: Schema.Literal("available", "conditional"),
|
||||
uses: Schema.Array(
|
||||
Schema.Literal(
|
||||
"step_input",
|
||||
"step_output_source",
|
||||
"state_target",
|
||||
"workflow_output",
|
||||
),
|
||||
),
|
||||
description: Schema.optional(Schema.String),
|
||||
reason: Schema.optional(Schema.String),
|
||||
});
|
||||
|
||||
const AuthoringStepContractSchema = Schema.Struct({
|
||||
step_id: Schema.String,
|
||||
label: Schema.String,
|
||||
description: Schema.optional(Schema.String),
|
||||
input_targets: Schema.optional(Schema.Array(AuthoringPathOptionSchema)),
|
||||
output_sources: Schema.optional(Schema.Array(AuthoringPathOptionSchema)),
|
||||
outcomes: Schema.optional(Schema.Array(Schema.String)),
|
||||
});
|
||||
|
||||
const AuthoringContractInventorySchema = Schema.Struct({
|
||||
workspace_id: Schema.String,
|
||||
revision: PositiveIntegerSchema,
|
||||
selected_step_id: Schema.NullOr(Schema.String),
|
||||
readable_sources: Schema.Array(AuthoringPathOptionSchema),
|
||||
step_input_targets: Schema.Array(AuthoringPathOptionSchema),
|
||||
step_output_sources: Schema.Array(AuthoringPathOptionSchema),
|
||||
state_targets: Schema.Array(AuthoringPathOptionSchema),
|
||||
workflow_output_targets: Schema.Array(AuthoringPathOptionSchema),
|
||||
entry_steps: Schema.Array(AuthoringStepContractSchema),
|
||||
workflow_outcomes: Schema.Array(Schema.String),
|
||||
warnings: Schema.Array(Schema.String),
|
||||
});
|
||||
|
||||
// Effect's empty Struct does not traverse excess keys; the impossible optional
|
||||
// field keeps the authored empty payload strict under onExcessProperty:error.
|
||||
const EmptyPayloadSchema = Schema.Struct({
|
||||
@@ -478,6 +527,14 @@ export const authoredRpcSchemas = {
|
||||
}),
|
||||
success: DraftWorkspaceSchema,
|
||||
},
|
||||
"workflow.draft_workspaces.inspect_authoring_contract": {
|
||||
payload: Schema.Struct({
|
||||
workspace_id: Schema.String.pipe(Schema.minLength(1)),
|
||||
revision: PositiveIntegerSchema,
|
||||
selected_step_id: Schema.optional(Schema.NullOr(Schema.String)),
|
||||
}),
|
||||
success: Schema.Union(AuthoringContractInventorySchema, DraftWorkspaceSchema),
|
||||
},
|
||||
"workflow.draft_workspaces.create_empty": {
|
||||
payload: Schema.Struct({
|
||||
workspace_id: Schema.String.pipe(Schema.minLength(1)),
|
||||
@@ -490,6 +547,25 @@ export const authoredRpcSchemas = {
|
||||
}),
|
||||
success: DraftWorkspaceSchema,
|
||||
},
|
||||
"workflow.draft_workspaces.set_contract": {
|
||||
payload: Schema.Struct({
|
||||
workspace_id: Schema.String.pipe(Schema.minLength(1)),
|
||||
revision: PositiveIntegerSchema,
|
||||
input_schema: Schema.optional(Schema.NullOr(JsonObjectSchema)),
|
||||
state_schema: Schema.optional(Schema.NullOr(JsonObjectSchema)),
|
||||
output_schema: Schema.optional(Schema.NullOr(JsonObjectSchema)),
|
||||
outcomes: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))),
|
||||
}),
|
||||
success: DraftWorkspaceSchema,
|
||||
},
|
||||
"workflow.draft_workspaces.set_start": {
|
||||
payload: Schema.Struct({
|
||||
workspace_id: Schema.String.pipe(Schema.minLength(1)),
|
||||
revision: PositiveIntegerSchema,
|
||||
step_id: Schema.String.pipe(Schema.minLength(1)),
|
||||
}),
|
||||
success: DraftWorkspaceSchema,
|
||||
},
|
||||
"workflow.draft_workspaces.create_from_capability": {
|
||||
payload: Schema.Struct({
|
||||
workspace_id: Schema.String.pipe(Schema.minLength(1)),
|
||||
@@ -574,6 +650,14 @@ export const authoredRpcSchemas = {
|
||||
}),
|
||||
success: DraftWorkspaceSchema,
|
||||
},
|
||||
"workflow.draft_workspaces.set_workflow_output_bindings": {
|
||||
payload: Schema.Struct({
|
||||
workspace_id: Schema.String.pipe(Schema.minLength(1)),
|
||||
revision: PositiveIntegerSchema,
|
||||
bindings: Schema.Array(Schema.Union(InputPathBindingSchema, InputValueBindingSchema)),
|
||||
}),
|
||||
success: DraftWorkspaceSchema,
|
||||
},
|
||||
"workflow.draft_workspaces.validate": {
|
||||
payload: Schema.Struct({
|
||||
workspace_id: Schema.String.pipe(Schema.minLength(1)),
|
||||
|
||||
@@ -13,14 +13,22 @@ import {
|
||||
WorkflowDraftWorkspacesCreateFromCapabilityResultSchema,
|
||||
WorkflowDraftWorkspacesGetPayloadSchema,
|
||||
WorkflowDraftWorkspacesGetResultSchema,
|
||||
WorkflowDraftWorkspacesInspectAuthoringContractPayloadSchema,
|
||||
WorkflowDraftWorkspacesInspectAuthoringContractResultSchema,
|
||||
WorkflowDraftWorkspacesListPayloadSchema,
|
||||
WorkflowDraftWorkspacesListResultSchema,
|
||||
WorkflowDraftWorkspacesSetContractPayloadSchema,
|
||||
WorkflowDraftWorkspacesSetContractResultSchema,
|
||||
WorkflowDraftWorkspacesSetRoutePayloadSchema,
|
||||
WorkflowDraftWorkspacesSetRouteResultSchema,
|
||||
WorkflowDraftWorkspacesSetStartPayloadSchema,
|
||||
WorkflowDraftWorkspacesSetStartResultSchema,
|
||||
WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema,
|
||||
WorkflowDraftWorkspacesSetStepInputBindingsResultSchema,
|
||||
WorkflowDraftWorkspacesSetStepOutputBindingsPayloadSchema,
|
||||
WorkflowDraftWorkspacesSetStepOutputBindingsResultSchema,
|
||||
WorkflowDraftWorkspacesSetWorkflowOutputBindingsPayloadSchema,
|
||||
WorkflowDraftWorkspacesSetWorkflowOutputBindingsResultSchema,
|
||||
WorkflowDraftWorkspacesUpdateCapabilityStepPayloadSchema,
|
||||
WorkflowDraftWorkspacesUpdateCapabilityStepResultSchema,
|
||||
WorkflowDraftWorkspacesValidatePayloadSchema,
|
||||
@@ -203,6 +211,66 @@ const draftWorkspace = {
|
||||
},
|
||||
};
|
||||
|
||||
const authoringPathOption = {
|
||||
path: "input.title",
|
||||
label: "Title",
|
||||
origin: "workflow_input",
|
||||
schema: { type: "string" },
|
||||
required: true,
|
||||
availability: "available",
|
||||
uses: ["step_input"],
|
||||
};
|
||||
|
||||
const authoringContractInventory = {
|
||||
workspace_id: "console.demo",
|
||||
revision: 7,
|
||||
selected_step_id: "render",
|
||||
readable_sources: [authoringPathOption],
|
||||
step_input_targets: [
|
||||
{
|
||||
...authoringPathOption,
|
||||
path: "local.title",
|
||||
origin: "step_input",
|
||||
uses: ["step_input"],
|
||||
},
|
||||
],
|
||||
step_output_sources: [
|
||||
{
|
||||
...authoringPathOption,
|
||||
path: "local.markdown",
|
||||
origin: "step_output",
|
||||
uses: ["step_output_source"],
|
||||
},
|
||||
],
|
||||
state_targets: [
|
||||
{
|
||||
...authoringPathOption,
|
||||
path: "state.report",
|
||||
origin: "workflow_state",
|
||||
uses: ["state_target", "workflow_output"],
|
||||
},
|
||||
],
|
||||
workflow_output_targets: [
|
||||
{
|
||||
...authoringPathOption,
|
||||
path: "output.report",
|
||||
origin: "workflow_output",
|
||||
uses: ["workflow_output"],
|
||||
},
|
||||
],
|
||||
entry_steps: [
|
||||
{
|
||||
step_id: "render",
|
||||
label: "Render report",
|
||||
input_targets: [],
|
||||
output_sources: [],
|
||||
outcomes: ["ok", "error"],
|
||||
},
|
||||
],
|
||||
workflow_outcomes: ["ok", "error"],
|
||||
warnings: [],
|
||||
};
|
||||
|
||||
const createFromCapabilityResult = {
|
||||
...draftWorkspace,
|
||||
next_actions: {
|
||||
@@ -460,6 +528,22 @@ const parityCases: ReadonlyArray<ParityCase> = [
|
||||
draft: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.inspect_authoring_contract",
|
||||
payload: WorkflowDraftWorkspacesInspectAuthoringContractPayloadSchema,
|
||||
success: WorkflowDraftWorkspacesInspectAuthoringContractResultSchema,
|
||||
validPayload: {
|
||||
workspace_id: "console.demo",
|
||||
revision: 7,
|
||||
selected_step_id: "render",
|
||||
},
|
||||
invalidPayload: { workspace_id: "console.demo", revision: 0 },
|
||||
validSuccess: authoringContractInventory,
|
||||
invalidSuccess: {
|
||||
...authoringContractInventory,
|
||||
entry_steps: [{ step_id: "render" }],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.create_empty",
|
||||
payload: WorkflowDraftWorkspacesCreateEmptyPayloadSchema,
|
||||
@@ -590,6 +674,41 @@ const parityCases: ReadonlyArray<ParityCase> = [
|
||||
summary: { ...draftWorkspace.summary, steps: [1] },
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.set_contract",
|
||||
payload: WorkflowDraftWorkspacesSetContractPayloadSchema,
|
||||
success: WorkflowDraftWorkspacesSetContractResultSchema,
|
||||
validPayload: {
|
||||
workspace_id: "console.demo",
|
||||
revision: 7,
|
||||
input_schema: { type: "object" },
|
||||
state_schema: { type: "object" },
|
||||
output_schema: { type: "object" },
|
||||
outcomes: ["ok", "error"],
|
||||
},
|
||||
invalidPayload: { workspace_id: "console.demo", revision: 0 },
|
||||
validSuccess: draftWorkspace,
|
||||
invalidSuccess: {
|
||||
...draftWorkspace,
|
||||
summary: { ...draftWorkspace.summary, steps: [1] },
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.set_start",
|
||||
payload: WorkflowDraftWorkspacesSetStartPayloadSchema,
|
||||
success: WorkflowDraftWorkspacesSetStartResultSchema,
|
||||
validPayload: {
|
||||
workspace_id: "console.demo",
|
||||
revision: 7,
|
||||
step_id: "render",
|
||||
},
|
||||
invalidPayload: { workspace_id: "console.demo", revision: 7, step_id: "" },
|
||||
validSuccess: draftWorkspace,
|
||||
invalidSuccess: {
|
||||
...draftWorkspace,
|
||||
summary: { ...draftWorkspace.summary, steps: [1] },
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.set_step_input_bindings",
|
||||
payload: WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema,
|
||||
@@ -640,6 +759,29 @@ const parityCases: ReadonlyArray<ParityCase> = [
|
||||
summary: { ...draftWorkspace.summary, steps: [1] },
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.set_workflow_output_bindings",
|
||||
payload: WorkflowDraftWorkspacesSetWorkflowOutputBindingsPayloadSchema,
|
||||
success: WorkflowDraftWorkspacesSetWorkflowOutputBindingsResultSchema,
|
||||
validPayload: {
|
||||
workspace_id: "console.demo",
|
||||
revision: 7,
|
||||
bindings: [
|
||||
{ path: "state.report", target: "report" },
|
||||
{ target: "format", value: "markdown" },
|
||||
],
|
||||
},
|
||||
invalidPayload: {
|
||||
workspace_id: "console.demo",
|
||||
revision: 7,
|
||||
bindings: [{ expression: { kind: "literal", value: "bad" } }],
|
||||
},
|
||||
validSuccess: draftWorkspace,
|
||||
invalidSuccess: {
|
||||
...draftWorkspace,
|
||||
summary: { ...draftWorkspace.summary, steps: [1] },
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.validate",
|
||||
payload: WorkflowDraftWorkspacesValidatePayloadSchema,
|
||||
@@ -1188,13 +1330,17 @@ describe("authored RPC and manifest schema parity", () => {
|
||||
"workflow.capabilities.call",
|
||||
"workflow.draft_workspaces.list",
|
||||
"workflow.draft_workspaces.get",
|
||||
"workflow.draft_workspaces.inspect_authoring_contract",
|
||||
"workflow.draft_workspaces.create_empty",
|
||||
"workflow.draft_workspaces.create_from_capability",
|
||||
"workflow.draft_workspaces.add_step_from_capability",
|
||||
"workflow.draft_workspaces.update_capability_step",
|
||||
"workflow.draft_workspaces.set_route",
|
||||
"workflow.draft_workspaces.set_contract",
|
||||
"workflow.draft_workspaces.set_start",
|
||||
"workflow.draft_workspaces.set_step_input_bindings",
|
||||
"workflow.draft_workspaces.set_step_output_bindings",
|
||||
"workflow.draft_workspaces.set_workflow_output_bindings",
|
||||
"workflow.draft_workspaces.validate",
|
||||
"workflow.artifacts.list",
|
||||
"workflow.artifacts.inspect",
|
||||
@@ -1231,6 +1377,7 @@ describe("authored RPC and manifest schema parity", () => {
|
||||
"workflow.draft_workspaces.update_capability_step:payload:oneOf@#/components/schemas/InputPathBinding.properties.path",
|
||||
"workflow.draft_workspaces.set_step_input_bindings:payload:oneOf@#/components/schemas/InputPathBinding.properties.path",
|
||||
"workflow.draft_workspaces.set_step_output_bindings:payload:oneOf@#/components/schemas/OutputBinding.properties.source",
|
||||
"workflow.draft_workspaces.set_workflow_output_bindings:payload:oneOf@#/components/schemas/InputPathBinding.properties.path",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,10 +27,14 @@ describe("runtimeSchemasFor", () => {
|
||||
"workflow.draft_workspaces.create_empty",
|
||||
"workflow.draft_workspaces.create_from_capability",
|
||||
"workflow.draft_workspaces.get",
|
||||
"workflow.draft_workspaces.inspect_authoring_contract",
|
||||
"workflow.draft_workspaces.list",
|
||||
"workflow.draft_workspaces.set_contract",
|
||||
"workflow.draft_workspaces.set_route",
|
||||
"workflow.draft_workspaces.set_start",
|
||||
"workflow.draft_workspaces.set_step_input_bindings",
|
||||
"workflow.draft_workspaces.set_step_output_bindings",
|
||||
"workflow.draft_workspaces.set_workflow_output_bindings",
|
||||
"workflow.draft_workspaces.update_capability_step",
|
||||
"workflow.draft_workspaces.validate",
|
||||
"workflow.health",
|
||||
|
||||
Reference in New Issue
Block a user