From 1cb885bb329a51219924fcd7f2f7f3f90877a36c Mon Sep 17 00:00:00 2001 From: Morten Olsen Date: Sun, 18 May 2025 20:26:15 +0200 Subject: [PATCH] feat: improved input format --- docs/README.md | 11 ++++++++ src/execution/handlers/handlers.input.ts | 32 +++++++++++++++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/docs/README.md b/docs/README.md index cb675ae..cac6ae9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -338,6 +338,17 @@ The `::md` directive embeds another markdown document. - `hidden`: If present, the actual content (markdown) of the embedded document will not be rendered in the output. However, any `http` requests within the embedded document _are still processed_, and their `request` and `response` data become available in the parent document's templating context (via `requests.id` and `responses.id`). This is useful if you only want to execute the requests from an included file (e.g., a common setup sequence) and use their results, without displaying the embedded file's content. - Example: `::md[./setup_requests.md]{hidden}` +#### `::input[{name}]` Directive Options + +The `::input` directive is used to declare expected input variables + +- **Variable Name:** The first argument (required) is the name of the variable + - Example: `::input[myVariable]` will define `input.myVariable` +- `required`: If present it will require that the variable is provided +- `default={value}`: Defines the default value if no value has been provided +- `format=string|number|bool|json|date`: If provided the value will be parsed using the specified format +- `` + ## Command-Line Interface (CLI) The `httpmd` tool provides the following commands: diff --git a/src/execution/handlers/handlers.input.ts b/src/execution/handlers/handlers.input.ts index 6e6efb5..7c55352 100644 --- a/src/execution/handlers/handlers.input.ts +++ b/src/execution/handlers/handlers.input.ts @@ -22,10 +22,34 @@ const inputHandler: ExecutionHandler = ({ context.input[name] = node.attributes.default; } - if (node.attributes?.format === 'number' && context.input[name] !== undefined) { - context.input[name] = Number(context.input[name]); - if (context.input[name] !== undefined && isNaN(Number(context.input[name]))) { - throw new Error(`Input "${name}" must be a number, but got "${context.input[name]}"`); + if (node.attributes?.format && context.input[name] !== undefined) { + const format = node.attributes.format; + if (format === 'number') { + context.input[name] = Number(context.input[name]); + if (context.input[name] !== undefined && isNaN(Number(context.input[name]))) { + throw new Error(`Input "${name}" must be a number, but got "${context.input[name]}"`); + } + } + if (format === 'boolean') { + context.input[name] = context.input[name] === 'true'; + } + if (format === 'string') { + context.input[name] = String(context.input[name]); + } + if (format === 'json') { + try { + context.input[name] = JSON.parse(String(context.input[name])); + } catch (error) { + throw new Error(`Input "${name}" must be a valid JSON, but got "${context.input[name]}"`); + } + } + + if (format === 'date') { + const date = new Date(context.input[name] as string); + if (isNaN(date.getTime())) { + throw new Error(`Input "${name}" must be a valid date, but got "${context.input[name]}"`); + } + context.input[name] = date; } }