Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func buildDefinitions() map[string]*jsonschema.Schema {
"eventProperties": eventPropertiesDefinition,
propExport: exportDefinition,
"externalResource": externalResourceDefinition,
"emitTask": emitTaskDefinition,
"flowDirective": flowDirectiveDefinition,
"forkTask": forkTaskDefinition,
"forTask": forTaskDefinition,
Expand Down Expand Up @@ -760,6 +761,45 @@ var externalResourceDefinition = &jsonschema.Schema{
},
}

var emitTaskDefinition = &jsonschema.Schema{
Type: typeObject,
Title: "EmitTask",
Description: "Allows workflows to publish events to event brokers or messaging systems, " +
"facilitating communication and coordination between different components and services.",
Required: []string{"emit"},
UnevaluatedProperties: falseSchema(),
AllOf: []*jsonschema.Schema{
{Ref: SchemaRef("taskBase")},
{
Properties: map[string]*jsonschema.Schema{
"emit": {
Type: typeObject,
Title: "EmitTaskConfiguration",
Description: "The configuration of an event's emission.",
UnevaluatedProperties: falseSchema(),
Required: []string{"event"},
Properties: map[string]*jsonschema.Schema{
"event": {
AdditionalProperties: trueSchema(),
Type: typeObject,
Title: "EmitEventDefinition",
Description: "The definition of the event to emit.",
Properties: map[string]*jsonschema.Schema{
"with": {
Ref: SchemaRef("eventProperties"),
Title: "EmitEventWith",
Description: "Defines the properties of event to emit.",
Required: []string{"type"},
},
},
},
},
},
},
},
},
}

var flowDirectiveDefinition = &jsonschema.Schema{
Title: "FlowDirective",
Description: "Represents different transition options for a workflow.",
Expand Down Expand Up @@ -1344,6 +1384,7 @@ var taskDefinition = &jsonschema.Schema{
OneOf: []*jsonschema.Schema{
{Ref: SchemaRef("callTask")},
{Ref: SchemaRef("doTask")},
{Ref: SchemaRef("emitTask")},
{Ref: SchemaRef("forTask")},
{Ref: SchemaRef("forkTask")},
{Ref: SchemaRef("listenTask")},
Expand Down
114 changes: 108 additions & 6 deletions definitions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package schema

import (
"encoding/json"
"slices"
"testing"

Expand Down Expand Up @@ -101,6 +102,7 @@ func TestBuildDefinitionsKeys(t *testing.T) {
"doTask",
defDocumentMetadata,
"duration",
"emitTask",
propEndpoint,
propError,
"eventConsumptionStrategy",
Expand Down Expand Up @@ -134,19 +136,17 @@ func TestBuildDefinitionsKeys(t *testing.T) {
for _, key := range expected {
assert.Contains(t, defs, key, "buildDefinitions() should contain %q", key)
}

// emitTask is intentionally unsupported.
assert.NotContains(t, defs, "emitTask", "emitTask must not be present")
}

// TestTaskDefinitionOneOf verifies that taskDefinition references exactly the
// supported task types and excludes unsupported ones.
// TestTaskDefinitionOneOf verifies that taskDefinition references each of the
// supported task types.
func TestTaskDefinitionOneOf(t *testing.T) {
refs := schemaRefs(taskDefinition.OneOf)

supported := []string{
SchemaRef("callTask"),
SchemaRef("doTask"),
SchemaRef("emitTask"),
SchemaRef("forTask"),
SchemaRef("forkTask"),
SchemaRef("listenTask"),
Expand All @@ -162,7 +162,109 @@ func TestTaskDefinitionOneOf(t *testing.T) {
assert.Contains(t, refs, ref, "taskDefinition.OneOf should reference %q", ref)
}

assert.NotContains(t, refs, SchemaRef("emitTask"), "emitTask must not appear in taskDefinition.OneOf")
assert.Len(t, refs, len(supported), "taskDefinition.OneOf must not reference any unexpected task types")
}

// TestEmitTaskDefinitionShape verifies that emitTask mirrors the upstream Open
// Workflow Specification definition: it inherits taskBase, closes the task and
// emit levels to unknown keys, requires emit, emit.event and (when present)
// emit.event.with.type, and reuses the shared eventProperties definition.
func TestEmitTaskDefinitionShape(t *testing.T) {
e := emitTaskDefinition

assert.Equal(t, typeObject, e.Type)
assert.Equal(t, []string{"emit"}, e.Required, "emitTask must require 'emit'")
assert.Equal(t, falseSchema(), e.UnevaluatedProperties, "emitTask must reject unevaluated properties")

require.Len(t, e.AllOf, 2)
assert.Equal(t, SchemaRef("taskBase"), e.AllOf[0].Ref, "emitTask must inherit taskBase")

emit, ok := e.AllOf[1].Properties["emit"]
require.True(t, ok, "emit property must be present")
assert.Equal(t, typeObject, emit.Type)
assert.Equal(t, []string{testPropEvent}, emit.Required, "emit must require 'event'")
assert.Equal(t, falseSchema(), emit.UnevaluatedProperties, "emit must reject unevaluated properties")

event, ok := emit.Properties[testPropEvent]
require.True(t, ok, "emit.event property must be present")
assert.Equal(t, typeObject, event.Type)
assert.Empty(t, event.Required, "emit.event must not require any properties (upstream leaves 'with' optional)")
assert.Equal(t, trueSchema(), event.AdditionalProperties, "emit.event must allow additional properties")

with, ok := event.Properties[propWith]
require.True(t, ok, "emit.event.with property must be present")
assert.Equal(t, SchemaRef("eventProperties"), with.Ref, "emit.event.with must reference eventProperties")
assert.Equal(t, []string{propType}, with.Required, "emit.event.with must require 'type'")
}

// TestEmitTaskDefinitionJSON verifies that emitTask serialises to the upstream
// JSON Schema keywords within the built schema, in particular that the
// falseSchema/trueSchema helpers marshal to boolean schemas and that emitTask
// is part of the task union.
func TestEmitTaskDefinitionJSON(t *testing.T) {
s, err := BuildSchema("1.0.0", "json")
require.NoError(t, err)

raw, err := json.Marshal(s)
require.NoError(t, err)

var doc struct {
Defs map[string]json.RawMessage `json:"$defs"`
}
require.NoError(t, json.Unmarshal(raw, &doc))

var task struct {
OneOf []*jsonschema.Schema `json:"oneOf"`
}
require.NoError(t, json.Unmarshal(doc.Defs["task"], &task))
assert.Contains(t, schemaRefs(task.OneOf), SchemaRef("emitTask"), "$defs.task.oneOf must reference emitTask")

type withJSON struct {
Ref string `json:"$ref"`
Required []string `json:"required"`
}
type eventJSON struct {
Type string `json:"type"`
Required []string `json:"required"`
AdditionalProperties any `json:"additionalProperties"`
Properties map[string]withJSON `json:"properties"`
}
type emitJSON struct {
Type string `json:"type"`
Required []string `json:"required"`
UnevaluatedProperties any `json:"unevaluatedProperties"`
Properties map[string]eventJSON `json:"properties"`
}
var emitTaskJSON struct {
Type string `json:"type"`
Required []string `json:"required"`
UnevaluatedProperties any `json:"unevaluatedProperties"`
AllOf []struct {
Ref string `json:"$ref"`
Properties map[string]emitJSON `json:"properties"`
} `json:"allOf"`
}
require.NoError(t, json.Unmarshal(doc.Defs["emitTask"], &emitTaskJSON))

assert.Equal(t, typeObject, emitTaskJSON.Type)
assert.Equal(t, []string{"emit"}, emitTaskJSON.Required)
assert.Equal(t, false, emitTaskJSON.UnevaluatedProperties, "emitTask.unevaluatedProperties must marshal to false")
require.Len(t, emitTaskJSON.AllOf, 2)
assert.Equal(t, SchemaRef("taskBase"), emitTaskJSON.AllOf[0].Ref)

emit := emitTaskJSON.AllOf[1].Properties["emit"]
assert.Equal(t, typeObject, emit.Type)
assert.Equal(t, []string{testPropEvent}, emit.Required)
assert.Equal(t, false, emit.UnevaluatedProperties, "emit.unevaluatedProperties must marshal to false")

event := emit.Properties[testPropEvent]
assert.Equal(t, typeObject, event.Type)
assert.Empty(t, event.Required, "emit.event must not declare required properties")
assert.Equal(t, true, event.AdditionalProperties, "emit.event.additionalProperties must marshal to true")

with := event.Properties[propWith]
assert.Equal(t, SchemaRef("eventProperties"), with.Ref)
assert.Equal(t, []string{propType}, with.Required)
}

// TestDurationDefinitionShape verifies that duration supports only the object
Expand Down
48 changes: 48 additions & 0 deletions schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,51 @@
}
]
},
"emitTask": {
"type": "object",
"title": "EmitTask",
"description": "Allows workflows to publish events to event brokers or messaging systems, facilitating communication and coordination between different components and services.",
"required": [
"emit"
],
"unevaluatedProperties": false,
"allOf": [
{
"$ref": "#/$defs/taskBase"
},
{
"properties": {
"emit": {
"type": "object",
"properties": {
"event": {
"type": "object",
"properties": {
"with": {
"$ref": "#/$defs/eventProperties",
"title": "EmitEventWith",
"description": "Defines the properties of event to emit.",
"required": [
"type"
]
}
},
"title": "EmitEventDefinition",
"description": "The definition of the event to emit.",
"additionalProperties": true
}
},
"title": "EmitTaskConfiguration",
"description": "The configuration of an event's emission.",
"required": [
"event"
],
"unevaluatedProperties": false
}
}
}
]
},
"endpoint": {
"title": "Endpoint",
"description": "Represents an endpoint.",
Expand Down Expand Up @@ -1524,6 +1569,9 @@
{
"$ref": "#/$defs/doTask"
},
{
"$ref": "#/$defs/emitTask"
},
{
"$ref": "#/$defs/forTask"
},
Expand Down
33 changes: 33 additions & 0 deletions schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,38 @@ $defs:
type: integer
type: object
unevaluatedProperties: false
emitTask:
allOf:
- $ref: '#/$defs/taskBase'
- properties:
emit:
description: The configuration of an event's emission.
properties:
event:
additionalProperties: true
description: The definition of the event to emit.
properties:
with:
$ref: '#/$defs/eventProperties'
description: Defines the properties of event to emit.
required:
- type
title: EmitEventWith
title: EmitEventDefinition
type: object
required:
- event
title: EmitTaskConfiguration
type: object
unevaluatedProperties: false
description: Allows workflows to publish events to event brokers or messaging
systems, facilitating communication and coordination between different components
and services.
required:
- emit
title: EmitTask
type: object
unevaluatedProperties: false
endpoint:
description: Represents an endpoint.
oneOf:
Expand Down Expand Up @@ -1045,6 +1077,7 @@ $defs:
oneOf:
- $ref: '#/$defs/callTask'
- $ref: '#/$defs/doTask'
- $ref: '#/$defs/emitTask'
- $ref: '#/$defs/forTask'
- $ref: '#/$defs/forkTask'
- $ref: '#/$defs/listenTask'
Expand Down
Loading
Loading