diff --git a/workflow-parser/src/model/converter/events.ts b/workflow-parser/src/model/converter/events.ts index dcfede6..2d4ba2d 100644 --- a/workflow-parser/src/model/converter/events.ts +++ b/workflow-parser/src/model/converter/events.ts @@ -59,17 +59,24 @@ export function convertOn(context: TemplateContext, token: TemplateToken): Event // All other events are defined as mappings. During schema validation we already ensure that events // receive only known keys, so here we can focus on the values and whether they are valid. + const eventToken = item.value.assertMapping(`event ${eventName}`); + if (eventName === "workflow_call") { + result.workflow_call = convertEventWorkflowCall(context, eventToken); + continue; + } + + if (eventName === "workflow_dispatch") { + result.workflow_dispatch = convertEventWorkflowDispatchInputs(context, eventToken); + continue; + } result[eventName] = { ...convertPatternFilter("branches", eventToken), ...convertPatternFilter("tags", eventToken), ...convertPatternFilter("paths", eventToken), ...convertFilter("types", eventToken), - ...convertFilter("workflows", eventToken), - // workflow_call and workflow_dispatch share input parsing - ...convertEventWorkflowDispatchInputs(context, eventToken), - ...convertEventWorkflowCall(context, eventToken) + ...convertFilter("workflows", eventToken) }; } diff --git a/workflow-parser/src/model/converter/workflow-call.ts b/workflow-parser/src/model/converter/workflow-call.ts index bc699d1..bf204ea 100644 --- a/workflow-parser/src/model/converter/workflow-call.ts +++ b/workflow-parser/src/model/converter/workflow-call.ts @@ -1,7 +1,9 @@ import {TemplateContext} from "../../templates/template-context"; import {MappingToken, TemplateToken} from "../../templates/tokens"; import {isMapping} from "../../templates/tokens/type-guards"; -import {SecretConfig, WorkflowCallConfig} from "../workflow-template"; +import {SecretConfig, WorkflowCallConfig, InputConfig, InputType} from "../workflow-template"; +import {convertStringList} from "./string-list"; +import {ScalarToken} from "../../templates/tokens/scalar-token"; export function convertEventWorkflowCall(context: TemplateContext, token: MappingToken): WorkflowCallConfig { const result: WorkflowCallConfig = {}; @@ -11,7 +13,7 @@ export function convertEventWorkflowCall(context: TemplateContext, token: Mappin switch (key.value) { case "inputs": - // Ignore, these are handled by convertEventWorkflowDispatchInputs + result.inputs = convertWorkflowInputs(context, item.value.assertMapping("workflow dispatch inputs")); break; case "secrets": @@ -27,6 +29,94 @@ export function convertEventWorkflowCall(context: TemplateContext, token: Mappin return result; } +export function convertWorkflowInputs( + context: TemplateContext, + token: MappingToken +): { + [inputName: string]: InputConfig; +} { + const result: {[inputName: string]: InputConfig} = {}; + + for (const item of token) { + const inputName = item.key.assertString("input name"); + const inputMapping = item.value.assertMapping("input configuration"); + + result[inputName.value] = convertWorkflowInput(context, inputMapping); + } + + return result; +} + +export function convertWorkflowInput(context: TemplateContext, token: MappingToken): InputConfig { + const result: InputConfig = { + type: InputType.string // Default to string + }; + + let defaultValue: undefined | ScalarToken; + + for (const item of token) { + const key = item.key.assertString("workflow dispatch input key"); + + switch (key.value) { + case "description": + result.description = item.value.assertString("input description").value; + break; + + case "required": + result.required = item.value.assertBoolean("input required").value; + break; + + case "default": + defaultValue = item.value.assertScalar("input default"); + break; + + case "type": + result.type = InputType[item.value.assertString("input type").value as keyof typeof InputType]; + break; + + case "options": + result.options = convertStringList("input options", item.value.assertSequence("input options")); + break; + + default: + context.error(item.key, `Invalid key '${key.value}'`); + } + } + + // Validate default value + if (defaultValue !== undefined && !defaultValue.isExpression) { + try { + switch (result.type) { + case InputType.boolean: + result.default = defaultValue.assertBoolean("input default").value; + + break; + + case InputType.string: + case InputType.choice: + case InputType.environment: + result.default = defaultValue.assertString("input default").value; + break; + } + } catch (e) { + context.error(defaultValue, e); + } + } + + // Validate `options` for `choice` type + if (result.type === InputType.choice) { + if (result.options === undefined || result.options.length === 0) { + context.error(token, "Missing 'options' for choice input"); + } + } else { + if (result.options !== undefined) { + context.error(token, "Input type is not 'choice', but 'options' is defined"); + } + } + + return result; +} + function convertWorkflowCallSecrets( context: TemplateContext, token: MappingToken diff --git a/workflow-parser/src/model/workflow-template.ts b/workflow-parser/src/model/workflow-template.ts index ff3d172..d50fea9 100644 --- a/workflow-parser/src/model/workflow-template.ts +++ b/workflow-parser/src/model/workflow-template.ts @@ -158,7 +158,7 @@ export type WorkflowDispatchConfig = { }; export type WorkflowCallConfig = { - inputs?: {[inputName: string]: InputConfig}; + inputs?: {[inputName: string]: InputConfig & {default?: string | boolean | number | ScalarToken}}; secrets?: {[secretName: string]: SecretConfig}; // TODO - these are supported in C# and Go but not in TS yet // outputs: { [outputName: string]: OutputConfig }