diff --git a/constants.go b/constants.go index 92c1046..5c46403 100644 --- a/constants.go +++ b/constants.go @@ -111,7 +111,7 @@ const ( propRetryPolicy = "retryPolicy" propTaskQueue = "taskQueue" propType = "type" - propScheduleWorkflowName = "scheduleWorkflowName" + propScheduleWorkflowType = "scheduleWorkflowType" propScheduleID = "scheduleId" propScheduleInput = "scheduleInput" propSeconds = "seconds" diff --git a/definitions.go b/definitions.go index e4326ad..f7ead7a 100644 --- a/definitions.go +++ b/definitions.go @@ -413,11 +413,12 @@ var documentMetadataDefinition = &jsonschema.Schema{ Title: "ContinueAsNewMaxHistoryLength", Description: "Allows you to test the Continue-As-New functionality by specifying the max history length before triggering.", }, - propScheduleWorkflowName: { - Type: typeString, - Title: "ScheduleWorkflowName", - Description: "Set the workflow name to trigger - this will either be the document.workflowType or the Do task", - MinLength: utils.Ptr(1), + propScheduleWorkflowType: { + Type: typeString, + Title: "ScheduleWorkflowType", + Description: "The workflow type the schedule starts: the document.workflowType or a top-level Do task. " + + "Defaults to document.workflowType; required when every top-level task is a Do task.", + MinLength: utils.Ptr(1), }, propScheduleID: { Type: typeString, diff --git a/metadata_test.go b/metadata_test.go index 6b177c4..f9b6622 100644 --- a/metadata_test.go +++ b/metadata_test.go @@ -158,7 +158,7 @@ func TestDocumentMetadataDefinitionShape(t *testing.T) { for _, key := range []string{ propCanMaxHistory, - propScheduleWorkflowName, + propScheduleWorkflowType, propScheduleID, propScheduleInput, } { @@ -167,7 +167,8 @@ func TestDocumentMetadataDefinitionShape(t *testing.T) { } assert.Equal(t, typeInteger, def.Properties[propCanMaxHistory].Type) - assert.Equal(t, typeString, def.Properties[propScheduleWorkflowName].Type) + assert.Equal(t, typeString, def.Properties[propScheduleWorkflowType].Type) + assert.False(t, def.Properties[propScheduleWorkflowType].Deprecated) assert.Equal(t, typeString, def.Properties[propScheduleID].Type) assert.Equal(t, typeArray, def.Properties[propScheduleInput].Type) } @@ -224,8 +225,8 @@ func TestDocumentMetadata_Validation(t *testing.T) { }{ // ---- valid cases ---- { - name: "scheduleWorkflowName string is accepted", - meta: map[string]any{"scheduleWorkflowName": "my-workflow"}, + name: "scheduleWorkflowType string is accepted", + meta: map[string]any{propScheduleWorkflowType: testWorkflowType}, }, { name: "scheduleId string is accepted", @@ -262,8 +263,13 @@ func TestDocumentMetadata_Validation(t *testing.T) { // ---- invalid cases ---- { - name: "scheduleWorkflowName as integer is rejected", - meta: map[string]any{"scheduleWorkflowName": 42}, + name: "scheduleWorkflowType as integer is rejected", + meta: map[string]any{propScheduleWorkflowType: 42}, + expectError: true, + }, + { + name: "empty scheduleWorkflowType is rejected", + meta: map[string]any{propScheduleWorkflowType: ""}, expectError: true, }, { diff --git a/schema.go b/schema.go index f2bc917..e093ee4 100644 --- a/schema.go +++ b/schema.go @@ -36,22 +36,6 @@ func BuildSchema(version, format string) (*jsonschema.Schema, error) { Required: []string{propDo, propDocument}, Properties: schemaProperties, Defs: buildDefinitions(), - If: &jsonschema.Schema{ - Required: []string{"schedule"}, - }, - Then: &jsonschema.Schema{ - Required: []string{propDocument}, - Properties: map[string]*jsonschema.Schema{ - propDocument: { - Required: []string{propMetadata}, - Properties: map[string]*jsonschema.Schema{ - propMetadata: { - Required: []string{propScheduleWorkflowName}, - }, - }, - }, - }, - }, } // Validate the generated schema diff --git a/schema.json b/schema.json index 8bc006c..e33add0 100644 --- a/schema.json +++ b/schema.json @@ -517,10 +517,10 @@ "title": "ScheduleInput", "description": "Set the input." }, - "scheduleWorkflowName": { + "scheduleWorkflowType": { "type": "string", - "title": "ScheduleWorkflowName", - "description": "Set the workflow name to trigger - this will either be the document.workflowType or the Do task", + "title": "ScheduleWorkflowType", + "description": "The workflow type the schedule starts: the document.workflowType or a top-level Do task. Defaults to document.workflowType; required when every top-level task is a Do task.", "minLength": 1 } }, @@ -1834,29 +1834,5 @@ "required": [ "do", "document" - ], - "if": { - "required": [ - "schedule" - ] - }, - "then": { - "properties": { - "document": { - "properties": { - "metadata": { - "required": [ - "scheduleWorkflowName" - ] - } - }, - "required": [ - "metadata" - ] - } - }, - "required": [ - "document" - ] - } + ] } diff --git a/schema.yaml b/schema.yaml index 15944dc..ed93248 100644 --- a/schema.yaml +++ b/schema.yaml @@ -305,11 +305,12 @@ $defs: description: Set the input. title: ScheduleInput type: array - scheduleWorkflowName: - description: Set the workflow name to trigger - this will either be the document.workflowType - or the Do task + scheduleWorkflowType: + description: 'The workflow type the schedule starts: the document.workflowType + or a top-level Do task. Defaults to document.workflowType; required when + every top-level task is a Do task.' minLength: 1 - title: ScheduleWorkflowName + title: ScheduleWorkflowType type: string title: DocumentMetadata type: object @@ -1245,9 +1246,6 @@ $defs: $id: https://zigflow.dev/schema.yaml $schema: https://json-schema.org/draft/2020-12/schema description: JSON Schema for a Zigflow workflow definition. -if: - required: - - schedule properties: do: $ref: '#/$defs/taskList' @@ -1339,16 +1337,5 @@ properties: required: - do - document -then: - properties: - document: - properties: - metadata: - required: - - scheduleWorkflowName - required: - - metadata - required: - - document title: Zigflow type: object diff --git a/schema_test.go b/schema_test.go index 6d01839..986249c 100644 --- a/schema_test.go +++ b/schema_test.go @@ -23,7 +23,10 @@ import ( "github.com/stretchr/testify/require" ) -const testCronExpr = "0 0 * * *" +const ( + testCronExpr = "0 0 * * *" + testWorkflowType = "my-workflow" +) // minimalWorkflow returns a minimal structurally valid workflow document. // It is used as a baseline for validation tests so that each test only @@ -97,41 +100,28 @@ func TestSchema_DocumentFields(t *testing.T) { }) } -// TestSchema_ScheduleRequiresScheduleWorkflowName verifies the conditional -// if/then rule: when top-level schedule is present, document.metadata must -// exist and contain scheduleWorkflowName. -func TestSchema_ScheduleRequiresScheduleWorkflowName(t *testing.T) { +// TestSchema_ScheduleNeedsNoWorkflowKey verifies that a schedule validates +// without a workflow key: the runtime defaults it to document.workflowType, and +// only it can tell when that does not apply (every top-level task a Do task). +func TestSchema_ScheduleNeedsNoWorkflowKey(t *testing.T) { resolved := resolvedTestSchema(t) - t.Run("schedule present without document.metadata is rejected", func(t *testing.T) { - doc := minimalWorkflow() - doc["schedule"] = map[string]any{propCron: testCronExpr} - - err := resolved.Validate(doc) - assert.Error(t, err, "schedule without document.metadata must fail validation") - }) - - t.Run("schedule present with document.metadata but missing scheduleWorkflowName is rejected", func(t *testing.T) { - doc := minimalWorkflow() - doc["schedule"] = map[string]any{propCron: testCronExpr} - doc["document"].(map[string]any)["metadata"] = map[string]any{ - propScheduleID: "my-schedule", - } - - err := resolved.Validate(doc) - assert.Error(t, err, "schedule with document.metadata but no scheduleWorkflowName must fail validation") - }) - - t.Run("schedule present with valid document.metadata.scheduleWorkflowName is accepted", func(t *testing.T) { - doc := minimalWorkflow() - doc["schedule"] = map[string]any{propCron: testCronExpr} - doc["document"].(map[string]any)["metadata"] = map[string]any{ - propScheduleWorkflowName: "my-workflow", - } - - err := resolved.Validate(doc) - assert.NoError(t, err, "schedule with valid scheduleWorkflowName must pass validation") - }) + for name, meta := range map[string]map[string]any{ + "no document.metadata": nil, + "metadata without a key": {propScheduleID: "my-schedule"}, + "the workflow type key": {propScheduleWorkflowType: testWorkflowType}, + "the old key, now unlisted": {"scheduleWorkflowName": testWorkflowType}, + } { + t.Run(name, func(t *testing.T) { + doc := minimalWorkflow() + doc["schedule"] = map[string]any{propCron: testCronExpr} + if meta != nil { + doc["document"].(map[string]any)["metadata"] = meta + } + + assert.NoError(t, resolved.Validate(doc), "a schedule must validate with %s", name) + }) + } } // TestSchema_RejectsUnknownTopLevelProperties verifies that the root schema