From 5649bbd1a3ed4eab1ad8709791f52ff29f7ec9f1 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Mon, 4 May 2026 16:24:30 +0530 Subject: [PATCH 01/26] feat: add parameters field to Success and Failure in arrazo --- packages/core/src/types/arazzo.ts | 2 ++ packages/core/src/typings/arazzo.ts | 2 ++ packages/respect-core/src/arazzo-schema.ts | 2 ++ 3 files changed, 6 insertions(+) diff --git a/packages/core/src/types/arazzo.ts b/packages/core/src/types/arazzo.ts index db00fad02e..a294da30c8 100755 --- a/packages/core/src/types/arazzo.ts +++ b/packages/core/src/types/arazzo.ts @@ -397,6 +397,7 @@ const SuccessActionObject: NodeType = { description: 'The workflowId referencing an existing workflow within the Arazzo Description to transfer to upon success of the step. This field is only relevant when the type field value is "goto". If the referenced workflow is contained within an arazzo type sourceDescription, then the workflowId MUST be specified using a Runtime Expression (e.g., $sourceDescriptions..) to avoid ambiguity or potential clashes. This field is mutually exclusive to stepId.', }, + parameters: 'Parameters', criteria: listOf('CriterionObject', { description: 'A list of assertions to determine if this action SHALL be executed. Each assertion is described using a Criterion Object. All criteria assertions MUST be satisfied for the action to be executed.', @@ -450,6 +451,7 @@ const FailureActionObject: NodeType = { description: 'A non-negative integer indicating how many attempts to retry the step MAY be attempted before failing the overall step. If not specified then a single retry SHALL be attempted. This field only applies when the type field value is "retry". The retryLimit MUST be exhausted prior to executing subsequent failure actions.', }, + parameters: 'Parameters', criteria: listOf('CriterionObject', { description: 'A list of assertions to determine if this action SHALL be executed. Each assertion is described using a Criterion Object.', diff --git a/packages/core/src/typings/arazzo.ts b/packages/core/src/typings/arazzo.ts index bbc485461f..cf08b950fc 100644 --- a/packages/core/src/typings/arazzo.ts +++ b/packages/core/src/typings/arazzo.ts @@ -144,6 +144,7 @@ export interface OnSuccessObject { type: 'goto' | 'end'; stepId?: string; workflowId?: string; + parameters?: Parameter[]; criteria?: CriterionObject[]; } @@ -154,6 +155,7 @@ export interface OnFailureObject { stepId?: string; retryAfter?: number; retryLimit?: number; + parameters?: Parameter[]; criteria?: CriterionObject[]; } diff --git a/packages/respect-core/src/arazzo-schema.ts b/packages/respect-core/src/arazzo-schema.ts index 331cc1cb26..278a38c785 100644 --- a/packages/respect-core/src/arazzo-schema.ts +++ b/packages/respect-core/src/arazzo-schema.ts @@ -260,6 +260,7 @@ export const onSuccessObject = { type: { type: 'string', enum: ['goto', 'end'] }, stepId: { type: 'string' }, workflowId: { type: 'string' }, + parameters: parameters, criteria: criteriaObjects, }, additionalProperties: false, @@ -280,6 +281,7 @@ export const onFailureObject = { stepId: { type: 'string' }, retryAfter: { type: 'number', minimum: 0 }, retryLimit: { type: 'number', minimum: 0 }, + parameters: parameters, criteria: criteriaObjects, }, additionalProperties: false, From c46b071d330a705a9768695ede1ee90fad582688 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 7 May 2026 16:57:00 +0530 Subject: [PATCH 02/26] feat: added spec parameters for arrazo rules in config --- .../__tests__/__snapshots__/config-resolvers.test.ts.snap | 2 ++ packages/core/src/config/__tests__/load.test.ts | 6 ++++++ packages/core/src/config/all.ts | 1 + packages/core/src/config/minimal.ts | 1 + packages/core/src/config/recommended-strict.ts | 1 + packages/core/src/config/recommended.ts | 1 + packages/core/src/config/spec.ts | 1 + packages/core/src/types/redocly-yaml.ts | 1 + 8 files changed, 14 insertions(+) diff --git a/packages/core/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap b/packages/core/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap index ed14440bc6..5d8968b516 100644 --- a/packages/core/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +++ b/packages/core/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap @@ -20,6 +20,7 @@ exports[`resolveConfig > should ignore minimal from the root and read local file "sourceDescription-name-unique": "error", "sourceDescription-type": "error", "sourceDescriptions-not-empty": "error", + "spec-parameters-in-by-context": "warn", "step-onFailure-unique": "warn", "step-onSuccess-unique": "warn", "stepId-unique": "error", @@ -407,6 +408,7 @@ exports[`resolveConfig > should resolve extends with local file config which con "sourceDescription-name-unique": "error", "sourceDescription-type": "error", "sourceDescriptions-not-empty": "error", + "spec-parameters-in-by-context": "warn", "step-onFailure-unique": "warn", "step-onSuccess-unique": "warn", "stepId-unique": "error", diff --git a/packages/core/src/config/__tests__/load.test.ts b/packages/core/src/config/__tests__/load.test.ts index f4565a5cad..8b65f8a95b 100644 --- a/packages/core/src/config/__tests__/load.test.ts +++ b/packages/core/src/config/__tests__/load.test.ts @@ -142,6 +142,7 @@ describe('loadConfig', () => { "sourceDescription-name-unique": "off", "sourceDescription-type": "off", "sourceDescriptions-not-empty": "off", + "spec-parameters-in-by-context": "off", "step-onFailure-unique": "off", "step-onSuccess-unique": "off", "stepId-unique": "error", @@ -466,6 +467,7 @@ describe('loadConfig', () => { "sourceDescription-name-unique": "error", "sourceDescription-type": "error", "sourceDescriptions-not-empty": "error", + "spec-parameters-in-by-context": "warn", "step-onFailure-unique": "warn", "step-onSuccess-unique": "warn", "stepId-unique": "error", @@ -795,6 +797,7 @@ describe('loadConfig', () => { "sourceDescription-name-unique": "off", "sourceDescription-type": "off", "sourceDescriptions-not-empty": "off", + "spec-parameters-in-by-context": "off", "step-onFailure-unique": "off", "step-onSuccess-unique": "off", "stepId-unique": "error", @@ -1208,6 +1211,7 @@ describe('loadConfig', () => { "sourceDescription-name-unique": "off", "sourceDescription-type": "off", "sourceDescriptions-not-empty": "off", + "spec-parameters-in-by-context": "off", "step-onFailure-unique": "off", "step-onSuccess-unique": "off", "stepId-unique": "error", @@ -1532,6 +1536,7 @@ describe('loadConfig', () => { "sourceDescription-name-unique": "error", "sourceDescription-type": "error", "sourceDescriptions-not-empty": "error", + "spec-parameters-in-by-context": "warn", "step-onFailure-unique": "warn", "step-onSuccess-unique": "warn", "stepId-unique": "error", @@ -1861,6 +1866,7 @@ describe('loadConfig', () => { "sourceDescription-name-unique": "off", "sourceDescription-type": "off", "sourceDescriptions-not-empty": "off", + "spec-parameters-in-by-context": "off", "step-onFailure-unique": "off", "step-onSuccess-unique": "off", "stepId-unique": "error", diff --git a/packages/core/src/config/all.ts b/packages/core/src/config/all.ts index fbdd42c095..0b7e3405e1 100644 --- a/packages/core/src/config/all.ts +++ b/packages/core/src/config/all.ts @@ -294,6 +294,7 @@ const all: RawGovernanceConfig<'built-in'> = { 'sourceDescription-name-unique': 'error', 'sourceDescription-type': 'error', 'sourceDescriptions-not-empty': 'error', + 'spec-parameters-in-by-context': 'error', 'step-onFailure-unique': 'error', 'step-onSuccess-unique': 'error', 'stepId-unique': 'error', diff --git a/packages/core/src/config/minimal.ts b/packages/core/src/config/minimal.ts index 4a00e4964e..f5ef806256 100644 --- a/packages/core/src/config/minimal.ts +++ b/packages/core/src/config/minimal.ts @@ -273,6 +273,7 @@ const minimal: RawGovernanceConfig<'built-in'> = { 'sourceDescription-name-unique': 'off', 'sourceDescription-type': 'off', 'sourceDescriptions-not-empty': 'off', + 'spec-parameters-in-by-context': 'off', 'step-onFailure-unique': 'off', 'step-onSuccess-unique': 'off', 'stepId-unique': 'error', diff --git a/packages/core/src/config/recommended-strict.ts b/packages/core/src/config/recommended-strict.ts index 250a6d5636..08e46d1691 100644 --- a/packages/core/src/config/recommended-strict.ts +++ b/packages/core/src/config/recommended-strict.ts @@ -273,6 +273,7 @@ const recommendedStrict: RawGovernanceConfig<'built-in'> = { 'sourceDescription-name-unique': 'error', 'sourceDescription-type': 'error', 'sourceDescriptions-not-empty': 'error', + 'spec-parameters-in-by-context': 'error', 'step-onFailure-unique': 'error', 'step-onSuccess-unique': 'error', 'stepId-unique': 'error', diff --git a/packages/core/src/config/recommended.ts b/packages/core/src/config/recommended.ts index f3333ef029..286eeb93be 100644 --- a/packages/core/src/config/recommended.ts +++ b/packages/core/src/config/recommended.ts @@ -273,6 +273,7 @@ const recommended: RawGovernanceConfig<'built-in'> = { 'sourceDescription-name-unique': 'error', 'sourceDescription-type': 'error', 'sourceDescriptions-not-empty': 'error', + 'spec-parameters-in-by-context': 'warn', 'step-onFailure-unique': 'warn', 'step-onSuccess-unique': 'warn', 'stepId-unique': 'error', diff --git a/packages/core/src/config/spec.ts b/packages/core/src/config/spec.ts index bea4c5e16e..7688933ef3 100644 --- a/packages/core/src/config/spec.ts +++ b/packages/core/src/config/spec.ts @@ -273,6 +273,7 @@ const spec: RawGovernanceConfig<'built-in'> = { 'sourceDescription-name-unique': 'error', 'sourceDescription-type': 'error', 'sourceDescriptions-not-empty': 'error', + 'spec-parameters-in-by-context': 'error', 'step-onFailure-unique': 'error', 'step-onSuccess-unique': 'error', 'stepId-unique': 'error', diff --git a/packages/core/src/types/redocly-yaml.ts b/packages/core/src/types/redocly-yaml.ts index 2f223f1579..4b032bfda9 100644 --- a/packages/core/src/types/redocly-yaml.ts +++ b/packages/core/src/types/redocly-yaml.ts @@ -163,6 +163,7 @@ const builtInArazzo1Rules = [ 'workflow-dependsOn', 'outputs-defined', 'parameters-unique', + 'spec-parameters-in-by-context', 'step-onSuccess-unique', 'step-onFailure-unique', 'respect-supported-versions', From 613e9a388358ab9d02c732d84d226426d33e26e3 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Fri, 8 May 2026 15:35:04 +0530 Subject: [PATCH 03/26] feat: added specs parameters logic and test with changeset --- .changeset/swift-otters-wander.md | 7 + .../spec-parameters-in-by-context.test.ts | 388 ++++++++++++++++++ packages/core/src/rules/arazzo/index.ts | 2 + .../arazzo/spec-parameters-in-by-context.ts | 81 ++++ 4 files changed, 478 insertions(+) create mode 100644 .changeset/swift-otters-wander.md create mode 100644 packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts create mode 100644 packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts diff --git a/.changeset/swift-otters-wander.md b/.changeset/swift-otters-wander.md new file mode 100644 index 0000000000..c4cbbdce02 --- /dev/null +++ b/.changeset/swift-otters-wander.md @@ -0,0 +1,7 @@ +--- +"@redocly/openapi-core": minor +"@redocly/respect-core": minor +"@redocly/cli": minor +--- + +Added the `spec-parameters-in-by-context` Arazzo rule, which validates that a parameter's `in` field is specified when the parent step, success action, or failure action does not reference a `workflowId`. Extended success and failure action objects to accept a `parameters` property that maps to workflow inputs. diff --git a/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts b/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts new file mode 100644 index 0000000000..e3cd4558f6 --- /dev/null +++ b/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts @@ -0,0 +1,388 @@ +import { outdent } from 'outdent'; + +import { parseYamlToDocument, replaceSourceWithRef } from '../../../../__tests__/utils.js'; +import { createConfig } from '../../../config/index.js'; +import { lintDocument } from '../../../lint.js'; +import { BaseResolver } from '../../../resolve.js'; + +describe('Arazzo spec-parameters-in-by-context', () => { + it('should not report when step references operationId and parameters specify `in`', async () => { + const document = parseYamlToDocument( + outdent` + arazzo: '1.0.1' + info: + title: Cool API + version: 1.0.0 + sourceDescriptions: + - name: museum-api + type: openapi + url: openapi.yaml + workflows: + - workflowId: get-museum-hours + steps: + - stepId: get-museum-hours + operationId: museum-api.getMuseumHours + parameters: + - in: header + name: Secret + value: Basic Og== + `, + 'arazzo.yaml' + ); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document, + config: await createConfig({ + rules: { 'spec-parameters-in-by-context': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`); + }); + + it('should report when step references operationId but a parameter is missing `in`', async () => { + const document = parseYamlToDocument( + outdent` + arazzo: '1.0.1' + info: + title: Cool API + version: 1.0.0 + sourceDescriptions: + - name: museum-api + type: openapi + url: openapi.yaml + workflows: + - workflowId: get-museum-hours + steps: + - stepId: get-museum-hours + operationId: museum-api.getMuseumHours + parameters: + - name: Secret + value: Basic Og== + `, + 'arazzo.yaml' + ); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document, + config: await createConfig({ + rules: { 'spec-parameters-in-by-context': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(` + [ + { + "location": [ + { + "pointer": "#/workflows/0/steps/0/parameters/0", + "reportOnKey": false, + "source": "arazzo.yaml", + }, + ], + "message": "Parameter \`in\` field MUST be specified when the parent does not reference a \`workflowId\`.", + "ruleId": "spec-parameters-in-by-context", + "severity": "error", + "suggest": [], + }, + ] + `); + }); + + it('should report when step references workflowId and a parameter has `in`', async () => { + const document = parseYamlToDocument( + outdent` + arazzo: '1.0.1' + info: + title: Cool API + version: 1.0.0 + sourceDescriptions: + - name: museum-api + type: openapi + url: openapi.yaml + workflows: + - workflowId: outer + inputs: + type: object + properties: + token: + type: string + steps: + - stepId: call-inner + workflowId: inner + parameters: + - in: header + name: token + value: abc + - workflowId: inner + steps: + - stepId: noop + operationId: museum-api.getMuseumHours + `, + 'arazzo.yaml' + ); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document, + config: await createConfig({ + rules: { 'spec-parameters-in-by-context': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(` + [ + { + "location": [ + { + "pointer": "#/workflows/0/steps/0/parameters/0/in", + "reportOnKey": true, + "source": "arazzo.yaml", + }, + ], + "message": "Parameter \`in\` field MUST NOT be specified when the parent references a \`workflowId\`; parameters map to workflow inputs.", + "ruleId": "spec-parameters-in-by-context", + "severity": "error", + "suggest": [], + }, + ] + `); + }); + + it('should not report when step references workflowId and parameter has no `in`', async () => { + const document = parseYamlToDocument( + outdent` + arazzo: '1.0.1' + info: + title: Cool API + version: 1.0.0 + sourceDescriptions: + - name: museum-api + type: openapi + url: openapi.yaml + workflows: + - workflowId: outer + steps: + - stepId: call-inner + workflowId: inner + parameters: + - name: token + value: abc + - workflowId: inner + steps: + - stepId: noop + operationId: museum-api.getMuseumHours + `, + 'arazzo.yaml' + ); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document, + config: await createConfig({ + rules: { 'spec-parameters-in-by-context': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`); + }); + + it('should report when onSuccess action with workflowId has a parameter with `in`', async () => { + const document = parseYamlToDocument( + outdent` + arazzo: '1.0.1' + info: + title: Cool API + version: 1.0.0 + sourceDescriptions: + - name: museum-api + type: openapi + url: openapi.yaml + workflows: + - workflowId: outer + steps: + - stepId: step-1 + operationId: museum-api.getMuseumHours + onSuccess: + - name: go-next + type: goto + workflowId: inner + parameters: + - in: header + name: token + value: abc + - workflowId: inner + steps: + - stepId: noop + operationId: museum-api.getMuseumHours + `, + 'arazzo.yaml' + ); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document, + config: await createConfig({ + rules: { 'spec-parameters-in-by-context': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(` + [ + { + "location": [ + { + "pointer": "#/workflows/0/steps/0/onSuccess/0/parameters/0/in", + "reportOnKey": true, + "source": "arazzo.yaml", + }, + ], + "message": "Parameter \`in\` field MUST NOT be specified when the parent references a \`workflowId\`; parameters map to workflow inputs.", + "ruleId": "spec-parameters-in-by-context", + "severity": "error", + "suggest": [], + }, + ] + `); + }); + + it('should not report when onFailure action with workflowId has parameter without `in`', async () => { + const document = parseYamlToDocument( + outdent` + arazzo: '1.0.1' + info: + title: Cool API + version: 1.0.0 + sourceDescriptions: + - name: museum-api + type: openapi + url: openapi.yaml + workflows: + - workflowId: outer + steps: + - stepId: step-1 + operationId: museum-api.getMuseumHours + onFailure: + - name: recover + type: goto + workflowId: inner + parameters: + - name: token + value: abc + - workflowId: inner + steps: + - stepId: noop + operationId: museum-api.getMuseumHours + `, + 'arazzo.yaml' + ); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document, + config: await createConfig({ + rules: { 'spec-parameters-in-by-context': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`); + }); + + it('should report when onSuccess action without workflowId defines parameters', async () => { + const document = parseYamlToDocument( + outdent` + arazzo: '1.0.1' + info: + title: Cool API + version: 1.0.0 + sourceDescriptions: + - name: museum-api + type: openapi + url: openapi.yaml + workflows: + - workflowId: outer + steps: + - stepId: step-1 + operationId: museum-api.getMuseumHours + onSuccess: + - name: go-step + type: goto + stepId: step-2 + parameters: + - in: header + name: token + value: abc + - stepId: step-2 + operationId: museum-api.getMuseumHours + `, + 'arazzo.yaml' + ); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document, + config: await createConfig({ + rules: { 'spec-parameters-in-by-context': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(` + [ + { + "location": [ + { + "pointer": "#/workflows/0/steps/0/onSuccess/0/parameters", + "reportOnKey": true, + "source": "arazzo.yaml", + }, + ], + "message": "Parameters on success/failure actions are only valid when the action references a \`workflowId\`.", + "ruleId": "spec-parameters-in-by-context", + "severity": "error", + "suggest": [], + }, + ] + `); + }); + + it('should ignore reusable parameters (only `reference`)', async () => { + const document = parseYamlToDocument( + outdent` + arazzo: '1.0.1' + info: + title: Cool API + version: 1.0.0 + sourceDescriptions: + - name: museum-api + type: openapi + url: openapi.yaml + components: + parameters: + shared: + in: header + name: token + value: abc + workflows: + - workflowId: outer + steps: + - stepId: step-1 + operationId: museum-api.getMuseumHours + parameters: + - reference: $components.parameters.shared + `, + 'arazzo.yaml' + ); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document, + config: await createConfig({ + rules: { 'spec-parameters-in-by-context': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`); + }); +}); diff --git a/packages/core/src/rules/arazzo/index.ts b/packages/core/src/rules/arazzo/index.ts index 2242a76ee1..2a5073399d 100644 --- a/packages/core/src/rules/arazzo/index.ts +++ b/packages/core/src/rules/arazzo/index.ts @@ -20,6 +20,7 @@ import { ParametersUnique } from './parameters-unique.js'; import { RequestBodyReplacementsUnique } from './requestBody-replacements-unique.js'; import { SourceDescriptionsNameUnique } from './sourceDescriptions-name-unique.js'; import { SourceDescriptionsNotEmpty } from './sourceDescriptions-not-empty.js'; +import { SpecParametersInByContext } from './spec-parameters-in-by-context.js'; import { StepOnFailureUnique } from './step-onFailure-unique.js'; import { StepOnSuccessUnique } from './step-onSuccess-unique.js'; import { StepIdUnique } from './stepId-unique.js'; @@ -44,6 +45,7 @@ export const rules: Arazzo1RuleSet<'built-in'> = { 'sourceDescription-name-unique': SourceDescriptionsNameUnique, 'sourceDescription-type': SourceDescriptionType, 'sourceDescriptions-not-empty': SourceDescriptionsNotEmpty, + 'spec-parameters-in-by-context': SpecParametersInByContext, 'step-onFailure-unique': StepOnFailureUnique, 'step-onSuccess-unique': StepOnSuccessUnique, 'stepId-unique': StepIdUnique, diff --git a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts new file mode 100644 index 0000000000..5d9f0f9c07 --- /dev/null +++ b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts @@ -0,0 +1,81 @@ +import type { Arazzo1Rule } from '../../visitors.js'; +import type { UserContext } from '../../walk.js'; + +const IN_REQUIRED_MESSAGE = + "Parameter `in` field MUST be specified when the parent does not reference a `workflowId`."; +const IN_NOT_ALLOWED_MESSAGE = + "Parameter `in` field MUST NOT be specified when the parent references a `workflowId`; parameters map to workflow inputs."; +const ACTION_PARAMETERS_REQUIRE_WORKFLOW_ID = + 'Parameters on success/failure actions are only valid when the action references a `workflowId`.'; + +function isInlineParameter(parameter: any): boolean { + return parameter && typeof parameter === 'object' && !('reference' in parameter); +} + +function checkParameters( + parameters: any, + hasWorkflowId: boolean, + basePath: (string | number)[], + { report, location }: UserContext +) { + if (!Array.isArray(parameters)) return; + + for (let i = 0; i < parameters.length; i++) { + const parameter = parameters[i]; + if (!isInlineParameter(parameter)) continue; + + const hasIn = 'in' in parameter; + + if (hasWorkflowId && hasIn) { + report({ + message: IN_NOT_ALLOWED_MESSAGE, + location: location.child([...basePath, i, 'in']).key(), + }); + } else if (!hasWorkflowId && !hasIn) { + report({ + message: IN_REQUIRED_MESSAGE, + location: location.child([...basePath, i]), + }); + } + } +} + +export const SpecParametersInByContext: Arazzo1Rule = () => { + return { + Step: { + enter(step, ctx: UserContext) { + if (!step.parameters) return; + const hasWorkflowId = Boolean(step.workflowId); + checkParameters(step.parameters, hasWorkflowId, ['parameters'], ctx); + }, + }, + SuccessActionObject: { + enter(action, ctx: UserContext) { + if (!('parameters' in action) || !action.parameters) return; + + const hasWorkflowId = Boolean(action.workflowId); + if (!hasWorkflowId) { + ctx.report({ + message: ACTION_PARAMETERS_REQUIRE_WORKFLOW_ID, + location: ctx.location.child(['parameters']).key(), + }); + } + checkParameters(action.parameters, hasWorkflowId, ['parameters'], ctx); + }, + }, + FailureActionObject: { + enter(action, ctx: UserContext) { + if (!('parameters' in action) || !action.parameters) return; + + const hasWorkflowId = Boolean(action.workflowId); + if (!hasWorkflowId) { + ctx.report({ + message: ACTION_PARAMETERS_REQUIRE_WORKFLOW_ID, + location: ctx.location.child(['parameters']).key(), + }); + } + checkParameters(action.parameters, hasWorkflowId, ['parameters'], ctx); + }, + }, + }; +}; From e057bd1478b1455e3f475103bf3d0541fce0e713 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Mon, 18 May 2026 15:19:20 +0530 Subject: [PATCH 04/26] fix: address comments and added docs --- .../arazzo/spec-parameters-in-by-context.md | 127 ++++++++++++++++++ docs/@v2/rules/built-in-rules.md | 1 + docs/@v2/v2.sidebars.yaml | 1 + .../arazzo/spec-parameters-in-by-context.ts | 34 +++-- 4 files changed, 144 insertions(+), 19 deletions(-) create mode 100644 docs/@v2/rules/arazzo/spec-parameters-in-by-context.md diff --git a/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md b/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md new file mode 100644 index 0000000000..e1353fbbf6 --- /dev/null +++ b/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md @@ -0,0 +1,127 @@ +# spec-parameters-in-by-context + +Requires the `in` field on a parameter to be specified or omitted based on the parent context. + +| Arazzo | Compatibility | +| ------ | ------------- | +| 1.x | ✅ | + +## Design principles + +The Arazzo specification states that when a step, success action, or failure action specifies a `workflowId`, all parameters map to the referenced workflow's inputs and the `in` field MUST NOT be specified. +In every other case (for example, when a step specifies an `operationId`, `operationPath`, or `x-operation`), the `in` field MUST be specified on each parameter. + +This rule additionally enforces that success and failure action `parameters` are only valid when the action references a `workflowId`. + +## Configuration + +| Option | Type | Description | +| -------- | ------ | ------------------------------------------------------- | +| severity | string | Possible values: `off`, `warn`, `error`. Default `off`. | + +An example configuration: + +```yaml +rules: + spec-parameters-in-by-context: error +``` + +## Examples + +Given the following configuration: + +```yaml +rules: + spec-parameters-in-by-context: error +``` + +Example of a **correct** step referencing an `operationId` (each parameter declares `in`): + +```yaml Correct example - operationId +workflows: + - workflowId: get-museum-hours + steps: + - stepId: list-hours + operationId: listMuseumHours + parameters: + - in: query + name: startDate + value: '2024-01-01' +``` + +Example of a **correct** step referencing a `workflowId` (parameters map to workflow inputs, no `in` field): + +```yaml Correct example - workflowId +workflows: + - workflowId: buy-tickets + steps: + - stepId: reuse-hours-workflow + workflowId: get-museum-hours + parameters: + - name: startDate + value: '2024-01-01' +``` + +Example of a **correct** success action transferring to another workflow with mapped parameters: + +```yaml Correct example - success action +workflows: + - workflowId: buy-tickets + steps: + - stepId: purchase + operationId: createTicket + onSuccess: + - name: continue-to-hours + type: goto + workflowId: get-museum-hours + parameters: + - name: startDate + value: '2024-01-01' +``` + +Example of an **incorrect** step referencing a `workflowId` while declaring `in` on a parameter: + +```yaml Incorrect example - workflowId with `in` +workflows: + - workflowId: buy-tickets + steps: + - stepId: reuse-hours-workflow + workflowId: get-museum-hours + parameters: + - in: query + name: startDate + value: '2024-01-01' +``` + +Example of an **incorrect** step referencing an `operationId` while omitting `in`: + +```yaml Incorrect example - operationId without `in` +workflows: + - workflowId: get-museum-hours + steps: + - stepId: list-hours + operationId: listMuseumHours + parameters: + - name: startDate + value: '2024-01-01' +``` + +Example of an **incorrect** success action defining `parameters` without referencing a `workflowId`: + +```yaml Incorrect example - action without workflowId +workflows: + - workflowId: buy-tickets + steps: + - stepId: purchase + operationId: createTicket + onSuccess: + - name: end-with-params + type: end + parameters: + - name: startDate + value: '2024-01-01' +``` + +## Resources + +- [Rule source](https://github.com/Redocly/redocly-cli/blob/main/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts) diff --git a/docs/@v2/rules/built-in-rules.md b/docs/@v2/rules/built-in-rules.md index c9891808a7..5ef74a75c4 100644 --- a/docs/@v2/rules/built-in-rules.md +++ b/docs/@v2/rules/built-in-rules.md @@ -135,6 +135,7 @@ Within the Arazzo family of rules, there are rules for the main Arazzo specifica - [requestBody-replacements-unique](./arazzo/requestBody-replacements-unique.md): the `replacements` of the `requestBody` object must be unique - [sourceDescription-name-unique](./arazzo/sourceDescription-name-unique.md): the `name` property of the `sourceDescription` object must be unique across all source descriptions - [sourceDescription-type](./arazzo/sourceDescription-type.md): the `type` property of the `sourceDescription` object must be either `openapi` or `arazzo` +- [spec-parameters-in-by-context](./arazzo/spec-parameters-in-by-context.md): the parameter `in` field must be specified or omitted based on whether the parent step or action references a `workflowId` - [stepId-unique](./arazzo/stepId-unique.md): the `stepId` must be unique amongst all steps described in the workflow - [step-onFailure-unique](./arazzo/step-onFailure-unique.md): the `onFailure` actions of the `step` object must be unique - [step-onSuccess-unique](./arazzo/step-onSuccess-unique.md): the `onSuccess` actions of the `step` object must be unique diff --git a/docs/@v2/v2.sidebars.yaml b/docs/@v2/v2.sidebars.yaml index 2e11a05018..66b14f142b 100644 --- a/docs/@v2/v2.sidebars.yaml +++ b/docs/@v2/v2.sidebars.yaml @@ -168,6 +168,7 @@ - page: rules/arazzo/requestBody-replacements-unique.md - page: rules/arazzo/sourceDescription-name-unique.md - page: rules/arazzo/sourceDescription-type.md + - page: rules/arazzo/spec-parameters-in-by-context.md - page: rules/arazzo/stepId-unique.md - page: rules/arazzo/step-onFailure-unique.md - page: rules/arazzo/step-onSuccess-unique.md diff --git a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts index 5d9f0f9c07..159ff36bc2 100644 --- a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts +++ b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts @@ -1,13 +1,6 @@ import type { Arazzo1Rule } from '../../visitors.js'; import type { UserContext } from '../../walk.js'; -const IN_REQUIRED_MESSAGE = - "Parameter `in` field MUST be specified when the parent does not reference a `workflowId`."; -const IN_NOT_ALLOWED_MESSAGE = - "Parameter `in` field MUST NOT be specified when the parent references a `workflowId`; parameters map to workflow inputs."; -const ACTION_PARAMETERS_REQUIRE_WORKFLOW_ID = - 'Parameters on success/failure actions are only valid when the action references a `workflowId`.'; - function isInlineParameter(parameter: any): boolean { return parameter && typeof parameter === 'object' && !('reference' in parameter); } @@ -28,12 +21,14 @@ function checkParameters( if (hasWorkflowId && hasIn) { report({ - message: IN_NOT_ALLOWED_MESSAGE, + message: + 'Parameter `in` field MUST NOT be specified when the parent references a `workflowId`; parameters map to workflow inputs.', location: location.child([...basePath, i, 'in']).key(), }); } else if (!hasWorkflowId && !hasIn) { report({ - message: IN_REQUIRED_MESSAGE, + message: + 'Parameter `in` field MUST be specified when the parent does not reference a `workflowId`.', location: location.child([...basePath, i]), }); } @@ -45,36 +40,37 @@ export const SpecParametersInByContext: Arazzo1Rule = () => { Step: { enter(step, ctx: UserContext) { if (!step.parameters) return; - const hasWorkflowId = Boolean(step.workflowId); - checkParameters(step.parameters, hasWorkflowId, ['parameters'], ctx); + checkParameters(step.parameters, Boolean(step.workflowId), ['parameters'], ctx); }, }, SuccessActionObject: { enter(action, ctx: UserContext) { if (!('parameters' in action) || !action.parameters) return; - const hasWorkflowId = Boolean(action.workflowId); - if (!hasWorkflowId) { + if (!action.workflowId) { ctx.report({ - message: ACTION_PARAMETERS_REQUIRE_WORKFLOW_ID, + message: + 'Parameters on success/failure actions are only valid when the action references a `workflowId`.', location: ctx.location.child(['parameters']).key(), }); + return; } - checkParameters(action.parameters, hasWorkflowId, ['parameters'], ctx); + checkParameters(action.parameters, true, ['parameters'], ctx); }, }, FailureActionObject: { enter(action, ctx: UserContext) { if (!('parameters' in action) || !action.parameters) return; - const hasWorkflowId = Boolean(action.workflowId); - if (!hasWorkflowId) { + if (!action.workflowId) { ctx.report({ - message: ACTION_PARAMETERS_REQUIRE_WORKFLOW_ID, + message: + 'Parameters on success/failure actions are only valid when the action references a `workflowId`.', location: ctx.location.child(['parameters']).key(), }); + return; } - checkParameters(action.parameters, hasWorkflowId, ['parameters'], ctx); + checkParameters(action.parameters, true, ['parameters'], ctx); }, }, }; From 28c0ac13f4953e085b0d0ea9e95de511471202ba Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Mon, 18 May 2026 15:26:30 +0530 Subject: [PATCH 05/26] fix: failing markdownlint failing test --- docs/@v2/rules/arazzo/spec-parameters-in-by-context.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md b/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md index e1353fbbf6..60ffb2d13d 100644 --- a/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md +++ b/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md @@ -83,12 +83,12 @@ Example of an **incorrect** step referencing a `workflowId` while declaring `in` ```yaml Incorrect example - workflowId with `in` workflows: - - workflowId: buy-tickets +- workflowId: buy-tickets steps: - - stepId: reuse-hours-workflow + - stepId: reuse-hours-workflow workflowId: get-museum-hours parameters: - - in: query + - in: query name: startDate value: '2024-01-01' ``` From c3223dc0aab75f17886af7b4215cbbfc32aa9c78 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Mon, 18 May 2026 15:30:15 +0530 Subject: [PATCH 06/26] fix: markdownlint failing test --- .../arazzo/spec-parameters-in-by-context.md | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md b/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md index 60ffb2d13d..5c495bdd8f 100644 --- a/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md +++ b/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md @@ -24,9 +24,7 @@ An example configuration: ```yaml rules: spec-parameters-in-by-context: error -``` - -## Examples +```## Examples Given the following configuration: @@ -37,7 +35,8 @@ rules: Example of a **correct** step referencing an `operationId` (each parameter declares `in`): -```yaml Correct example - operationId +```yaml +# Correct example - operationId workflows: - workflowId: get-museum-hours steps: @@ -51,7 +50,8 @@ workflows: Example of a **correct** step referencing a `workflowId` (parameters map to workflow inputs, no `in` field): -```yaml Correct example - workflowId +```yaml +# Correct example - workflowId workflows: - workflowId: buy-tickets steps: @@ -64,7 +64,8 @@ workflows: Example of a **correct** success action transferring to another workflow with mapped parameters: -```yaml Correct example - success action +```yaml +# Correct example - success action workflows: - workflowId: buy-tickets steps: @@ -81,21 +82,23 @@ workflows: Example of an **incorrect** step referencing a `workflowId` while declaring `in` on a parameter: -```yaml Incorrect example - workflowId with `in` +```yaml +# Incorrect example - workflowId with `in` workflows: -- workflowId: buy-tickets + - workflowId: buy-tickets steps: - - stepId: reuse-hours-workflow + - stepId: reuse-hours-workflow workflowId: get-museum-hours parameters: - - in: query + - in: query name: startDate value: '2024-01-01' ``` Example of an **incorrect** step referencing an `operationId` while omitting `in`: -```yaml Incorrect example - operationId without `in` +```yaml +# Incorrect example - operationId without `in` workflows: - workflowId: get-museum-hours steps: @@ -108,7 +111,8 @@ workflows: Example of an **incorrect** success action defining `parameters` without referencing a `workflowId`: -```yaml Incorrect example - action without workflowId +```yaml +# Incorrect example - action without workflowId workflows: - workflowId: buy-tickets steps: @@ -124,4 +128,4 @@ workflows: ## Resources -- [Rule source](https://github.com/Redocly/redocly-cli/blob/main/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts) +- [Rule source](https://github.com/Redocly/redocly-cli/blob/main/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts) \ No newline at end of file From f3ffcdde1c3c26d6ecfed290b25336dfe3b1ebbc Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Wed, 20 May 2026 15:15:46 +0530 Subject: [PATCH 07/26] fix: address comments and updated changset and used paramters interface --- .changeset/swift-otters-wander.md | 4 ++- .../arazzo/spec-parameters-in-by-context.md | 5 ++-- .../arazzo/spec-parameters-in-by-context.ts | 29 ++++++++++++------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/.changeset/swift-otters-wander.md b/.changeset/swift-otters-wander.md index c4cbbdce02..9d2c5299df 100644 --- a/.changeset/swift-otters-wander.md +++ b/.changeset/swift-otters-wander.md @@ -4,4 +4,6 @@ "@redocly/cli": minor --- -Added the `spec-parameters-in-by-context` Arazzo rule, which validates that a parameter's `in` field is specified when the parent step, success action, or failure action does not reference a `workflowId`. Extended success and failure action objects to accept a `parameters` property that maps to workflow inputs. +Added the `spec-parameters-in-by-context` Arazzo rule, which validates that a parameter's `in` field is specified when the parent workflow, step, success action, or failure action does not reference a `workflowId`. Extended success and failure action objects to accept a `parameters` property that maps to workflow inputs. + +Note: because this rule is part of the `spec` ruleset (and is set to `error` in `recommended-strict` and `all`), linting Arazzo descriptions that omit a required `in` field, or that specify `in` when referencing a `workflowId`, may now report new errors. diff --git a/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md b/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md index 5c495bdd8f..71555cc983 100644 --- a/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md +++ b/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md @@ -9,7 +9,7 @@ Requires the `in` field on a parameter to be specified or omitted based on the p ## Design principles The Arazzo specification states that when a step, success action, or failure action specifies a `workflowId`, all parameters map to the referenced workflow's inputs and the `in` field MUST NOT be specified. -In every other case (for example, when a step specifies an `operationId`, `operationPath`, or `x-operation`), the `in` field MUST be specified on each parameter. +In every other case (for example, when a step specifies an `operationId`, `operationPath`, or `x-operation`, or for parameters defined at the workflow level), the `in` field MUST be specified on each parameter. This rule additionally enforces that success and failure action `parameters` are only valid when the action references a `workflowId`. @@ -24,6 +24,7 @@ An example configuration: ```yaml rules: spec-parameters-in-by-context: error + ```## Examples Given the following configuration: @@ -128,4 +129,4 @@ workflows: ## Resources -- [Rule source](https://github.com/Redocly/redocly-cli/blob/main/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts) \ No newline at end of file +- [Rule source](https://github.com/Redocly/redocly-cli/blob/main/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts) diff --git a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts index 159ff36bc2..5f54d2342a 100644 --- a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts +++ b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts @@ -1,14 +1,14 @@ +import type { Parameter } from '../../typings/arazzo.js'; import type { Arazzo1Rule } from '../../visitors.js'; import type { UserContext } from '../../walk.js'; -function isInlineParameter(parameter: any): boolean { - return parameter && typeof parameter === 'object' && !('reference' in parameter); +function isInlineParameter(parameter: Parameter): boolean { + return Boolean(parameter) && typeof parameter === 'object' && !('reference' in parameter); } function checkParameters( - parameters: any, + parameters: Parameter[], hasWorkflowId: boolean, - basePath: (string | number)[], { report, location }: UserContext ) { if (!Array.isArray(parameters)) return; @@ -23,13 +23,13 @@ function checkParameters( report({ message: 'Parameter `in` field MUST NOT be specified when the parent references a `workflowId`; parameters map to workflow inputs.', - location: location.child([...basePath, i, 'in']).key(), + location: location.child(['parameters', i, 'in']).key(), }); } else if (!hasWorkflowId && !hasIn) { report({ message: 'Parameter `in` field MUST be specified when the parent does not reference a `workflowId`.', - location: location.child([...basePath, i]), + location: location.child(['parameters', i]), }); } } @@ -37,15 +37,22 @@ function checkParameters( export const SpecParametersInByContext: Arazzo1Rule = () => { return { + Workflow: { + enter(workflow, ctx: UserContext) { + if (!workflow.parameters) return; + // A workflow never references another workflow, so `in` is always required. + checkParameters(workflow.parameters, false, ctx); + }, + }, Step: { enter(step, ctx: UserContext) { if (!step.parameters) return; - checkParameters(step.parameters, Boolean(step.workflowId), ['parameters'], ctx); + checkParameters(step.parameters, Boolean(step.workflowId), ctx); }, }, SuccessActionObject: { enter(action, ctx: UserContext) { - if (!('parameters' in action) || !action.parameters) return; + if (!action.parameters) return; if (!action.workflowId) { ctx.report({ @@ -55,12 +62,12 @@ export const SpecParametersInByContext: Arazzo1Rule = () => { }); return; } - checkParameters(action.parameters, true, ['parameters'], ctx); + checkParameters(action.parameters, true, ctx); }, }, FailureActionObject: { enter(action, ctx: UserContext) { - if (!('parameters' in action) || !action.parameters) return; + if (!action.parameters) return; if (!action.workflowId) { ctx.report({ @@ -70,7 +77,7 @@ export const SpecParametersInByContext: Arazzo1Rule = () => { }); return; } - checkParameters(action.parameters, true, ['parameters'], ctx); + checkParameters(action.parameters, true, ctx); }, }, }; From db9eb6b8937edde70ba00c0438f579d27d8d1ddc Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Wed, 20 May 2026 15:18:15 +0530 Subject: [PATCH 08/26] fix: added spec parameter test --- .../spec-parameters-in-by-context.test.ts | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts b/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts index e3cd4558f6..fe0ed07d38 100644 --- a/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts +++ b/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts @@ -385,4 +385,89 @@ describe('Arazzo spec-parameters-in-by-context', () => { expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`); }); + + it('should not report when workflow-level parameters specify `in`', async () => { + const document = parseYamlToDocument( + outdent` + arazzo: '1.0.1' + info: + title: Cool API + version: 1.0.0 + sourceDescriptions: + - name: museum-api + type: openapi + url: openapi.yaml + workflows: + - workflowId: get-museum-hours + parameters: + - in: header + name: Secret + value: Basic Og== + steps: + - stepId: get-museum-hours + operationId: museum-api.getMuseumHours + `, + 'arazzo.yaml' + ); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document, + config: await createConfig({ + rules: { 'spec-parameters-in-by-context': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`); + }); + + it('should report when workflow-level parameter is missing `in`', async () => { + const document = parseYamlToDocument( + outdent` + arazzo: '1.0.1' + info: + title: Cool API + version: 1.0.0 + sourceDescriptions: + - name: museum-api + type: openapi + url: openapi.yaml + workflows: + - workflowId: get-museum-hours + parameters: + - name: Secret + value: Basic Og== + steps: + - stepId: get-museum-hours + operationId: museum-api.getMuseumHours + `, + 'arazzo.yaml' + ); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document, + config: await createConfig({ + rules: { 'spec-parameters-in-by-context': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(` + [ + { + "location": [ + { + "pointer": "#/workflows/0/parameters/0", + "reportOnKey": false, + "source": "arazzo.yaml", + }, + ], + "message": "Parameter \`in\` field MUST be specified when the parent does not reference a \`workflowId\`.", + "ruleId": "spec-parameters-in-by-context", + "severity": "error", + "suggest": [], + }, + ] + `); + }); }); From 83a27cb56bd6eea5309f43996d02555680ab67c4 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Wed, 20 May 2026 15:27:04 +0530 Subject: [PATCH 09/26] fix: minor lint fix --- docs/@v2/rules/arazzo/spec-parameters-in-by-context.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md b/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md index 71555cc983..6001616cbc 100644 --- a/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md +++ b/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md @@ -24,8 +24,9 @@ An example configuration: ```yaml rules: spec-parameters-in-by-context: error +``` -```## Examples +## Examples Given the following configuration: From a0470a41f5fe932812781a7d75de36b613571ef6 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 28 May 2026 15:44:27 +0530 Subject: [PATCH 10/26] fix: address comment and fix verbiage --- .../core/src/rules/arazzo/spec-parameters-in-by-context.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts index 5f54d2342a..00a838c042 100644 --- a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts +++ b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts @@ -57,7 +57,7 @@ export const SpecParametersInByContext: Arazzo1Rule = () => { if (!action.workflowId) { ctx.report({ message: - 'Parameters on success/failure actions are only valid when the action references a `workflowId`.', + 'Parameters on success actions are only valid when the action references a `workflowId`.', location: ctx.location.child(['parameters']).key(), }); return; @@ -72,7 +72,7 @@ export const SpecParametersInByContext: Arazzo1Rule = () => { if (!action.workflowId) { ctx.report({ message: - 'Parameters on success/failure actions are only valid when the action references a `workflowId`.', + 'Parameters on failure actions are only valid when the action references a `workflowId`.', location: ctx.location.child(['parameters']).key(), }); return; From 667cf829c68255a1e628a566875fcddf6efd7379 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 28 May 2026 15:52:49 +0530 Subject: [PATCH 11/26] feat: added actionParameters in replacement of parameters --- .../src/rules/arazzo/spec-parameters-in-by-context.ts | 4 ---- packages/core/src/typings/arazzo.ts | 10 ++++++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts index 00a838c042..c64193259f 100644 --- a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts +++ b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts @@ -60,9 +60,7 @@ export const SpecParametersInByContext: Arazzo1Rule = () => { 'Parameters on success actions are only valid when the action references a `workflowId`.', location: ctx.location.child(['parameters']).key(), }); - return; } - checkParameters(action.parameters, true, ctx); }, }, FailureActionObject: { @@ -75,9 +73,7 @@ export const SpecParametersInByContext: Arazzo1Rule = () => { 'Parameters on failure actions are only valid when the action references a `workflowId`.', location: ctx.location.child(['parameters']).key(), }); - return; } - checkParameters(action.parameters, true, ctx); }, }, }; diff --git a/packages/core/src/typings/arazzo.ts b/packages/core/src/typings/arazzo.ts index cf08b950fc..5a11a15484 100644 --- a/packages/core/src/typings/arazzo.ts +++ b/packages/core/src/typings/arazzo.ts @@ -37,6 +37,12 @@ export interface Parameter { reference?: string; } +export interface ActionParameter { + name: string; + value: string | number | boolean; + reference?: string; +} + export type ExtendedSecurity = | { schemeName: string; @@ -144,7 +150,7 @@ export interface OnSuccessObject { type: 'goto' | 'end'; stepId?: string; workflowId?: string; - parameters?: Parameter[]; + parameters?: ActionParameter[]; criteria?: CriterionObject[]; } @@ -155,7 +161,7 @@ export interface OnFailureObject { stepId?: string; retryAfter?: number; retryLimit?: number; - parameters?: Parameter[]; + parameters?: ActionParameter[]; criteria?: CriterionObject[]; } From af9e4bed59db9cc0f942d9280497caf85027665e Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 28 May 2026 16:00:35 +0530 Subject: [PATCH 12/26] feat: added actionParameters for workflow actions --- packages/core/src/types/arazzo.ts | 29 ++++++++++++++++++++-- packages/respect-core/src/arazzo-schema.ts | 26 +++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/packages/core/src/types/arazzo.ts b/packages/core/src/types/arazzo.ts index a294da30c8..7afbe4b333 100755 --- a/packages/core/src/types/arazzo.ts +++ b/packages/core/src/types/arazzo.ts @@ -161,6 +161,29 @@ const Parameters: NodeType = { } }, }; +const ActionParameter: NodeType = { + properties: { + name: { + type: 'string', + description: 'REQUIRED. The name of the parameter. Parameter names are case sensitive.', + }, + value: {}, + }, + required: ['name', 'value'], + extensionsPrefix: 'x-', + description: + 'Describes a single parameter passed to a workflow referenced by a success or failure action. Action parameters map to the referenced workflow inputs, so the `in` field MUST NOT be used.', +}; +const ActionParameters: NodeType = { + properties: {}, + items: (value: any) => { + if (value?.reference) { + return 'ReusableObject'; + } else { + return 'ActionParameter'; + } + }, +}; const Workflow: NodeType = { properties: { workflowId: { @@ -397,7 +420,7 @@ const SuccessActionObject: NodeType = { description: 'The workflowId referencing an existing workflow within the Arazzo Description to transfer to upon success of the step. This field is only relevant when the type field value is "goto". If the referenced workflow is contained within an arazzo type sourceDescription, then the workflowId MUST be specified using a Runtime Expression (e.g., $sourceDescriptions..) to avoid ambiguity or potential clashes. This field is mutually exclusive to stepId.', }, - parameters: 'Parameters', + parameters: 'ActionParameters', criteria: listOf('CriterionObject', { description: 'A list of assertions to determine if this action SHALL be executed. Each assertion is described using a Criterion Object. All criteria assertions MUST be satisfied for the action to be executed.', @@ -451,7 +474,7 @@ const FailureActionObject: NodeType = { description: 'A non-negative integer indicating how many attempts to retry the step MAY be attempted before failing the overall step. If not specified then a single retry SHALL be attempted. This field only applies when the type field value is "retry". The retryLimit MUST be exhausted prior to executing subsequent failure actions.', }, - parameters: 'Parameters', + parameters: 'ActionParameters', criteria: listOf('CriterionObject', { description: 'A list of assertions to determine if this action SHALL be executed. Each assertion is described using a Criterion Object.', @@ -480,6 +503,8 @@ export const Arazzo1Types: Record = { ArazzoSourceDescription, Parameters, Parameter, + ActionParameters, + ActionParameter, ReusableObject, Workflows, Workflow, diff --git a/packages/respect-core/src/arazzo-schema.ts b/packages/respect-core/src/arazzo-schema.ts index 278a38c785..7caeb9f214 100644 --- a/packages/respect-core/src/arazzo-schema.ts +++ b/packages/respect-core/src/arazzo-schema.ts @@ -173,6 +173,28 @@ const parameters = { type: 'array', items: parameter, } as const; +export const actionParameter = { + type: 'object', + oneOf: [ + { + type: 'object', + properties: { + name: { type: 'string' }, + value: { + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], + }, + 'x-allowReserved': { type: 'boolean' }, + }, + required: ['name', 'value'], + additionalProperties: false, + }, + reusableObject, + ], +} as const; +const actionParameters = { + type: 'array', + items: actionParameter, +} as const; export const infoObject = { type: 'object', properties: { @@ -260,7 +282,7 @@ export const onSuccessObject = { type: { type: 'string', enum: ['goto', 'end'] }, stepId: { type: 'string' }, workflowId: { type: 'string' }, - parameters: parameters, + parameters: actionParameters, criteria: criteriaObjects, }, additionalProperties: false, @@ -281,7 +303,7 @@ export const onFailureObject = { stepId: { type: 'string' }, retryAfter: { type: 'number', minimum: 0 }, retryLimit: { type: 'number', minimum: 0 }, - parameters: parameters, + parameters: actionParameters, criteria: criteriaObjects, }, additionalProperties: false, From 8b47027a45729b13f45141d4fdb9c06f82e3071a Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 28 May 2026 16:01:54 +0530 Subject: [PATCH 13/26] fix: updated tests --- .../__tests__/spec-parameters-in-by-context.test.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts b/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts index fe0ed07d38..c7bd14a82e 100644 --- a/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts +++ b/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts @@ -189,7 +189,7 @@ describe('Arazzo spec-parameters-in-by-context', () => { expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`); }); - it('should report when onSuccess action with workflowId has a parameter with `in`', async () => { + it('should report a struct error when an action parameter specifies `in`', async () => { const document = parseYamlToDocument( outdent` arazzo: '1.0.1' @@ -225,13 +225,14 @@ describe('Arazzo spec-parameters-in-by-context', () => { externalRefResolver: new BaseResolver(), document, config: await createConfig({ - rules: { 'spec-parameters-in-by-context': 'error' }, + rules: { struct: 'error' }, }), }); expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(` [ { + "from": undefined, "location": [ { "pointer": "#/workflows/0/steps/0/onSuccess/0/parameters/0/in", @@ -239,8 +240,8 @@ describe('Arazzo spec-parameters-in-by-context', () => { "source": "arazzo.yaml", }, ], - "message": "Parameter \`in\` field MUST NOT be specified when the parent references a \`workflowId\`; parameters map to workflow inputs.", - "ruleId": "spec-parameters-in-by-context", + "message": "Property \`in\` is not expected here.", + "ruleId": "struct", "severity": "error", "suggest": [], }, @@ -338,7 +339,7 @@ describe('Arazzo spec-parameters-in-by-context', () => { "source": "arazzo.yaml", }, ], - "message": "Parameters on success/failure actions are only valid when the action references a \`workflowId\`.", + "message": "Parameters on success actions are only valid when the action references a \`workflowId\`.", "ruleId": "spec-parameters-in-by-context", "severity": "error", "suggest": [], From d0fc6830a4d74891bd3b27d53b01987eae97e52f Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 28 May 2026 16:28:10 +0530 Subject: [PATCH 14/26] feat: added mapParametersToWorkflowInputs function in run step for better handlingg --- .../src/modules/flow-runner/run-step.ts | 74 +++++++++++++------ 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/packages/respect-core/src/modules/flow-runner/run-step.ts b/packages/respect-core/src/modules/flow-runner/run-step.ts index 7493a89acc..71e726e410 100644 --- a/packages/respect-core/src/modules/flow-runner/run-step.ts +++ b/packages/respect-core/src/modules/flow-runner/run-step.ts @@ -33,6 +33,32 @@ import { prepareRequest, type RequestData } from './prepare-request.js'; import { runWorkflow, resolveWorkflowContext } from './runner.js'; import { checkCriteria } from './success-criteria/index.js'; +function mapParametersToWorkflowInputs({ + parameters, + ctx, + workflowId, +}: { + parameters: ResolvedParameter[]; + ctx: TestContext; + workflowId: string | undefined; +}): Record { + return parameters.filter(isParameterWithoutIn).reduce((acc, parameter: ParameterWithoutIn) => { + const ctxWithInputs = { + ...ctx, + $inputs: { + ...ctx.$inputs, + ...(workflowId ? ctx.$workflows[workflowId]?.inputs : {}), + }, + }; + acc[parameter.name] = getValueFromContext({ + value: parameter.value, + ctx: ctxWithInputs, + logger: ctx.options.logger, + }); + return acc; + }, {} as Record); +} + export async function runStep({ step, ctx, @@ -89,25 +115,11 @@ export async function runStep({ if (resolvedParameters && resolvedParameters.length > 0) { // When the step in context specifies a workflowId, then all parameters without `in` maps to workflow inputs. - const workflowInputParameters = resolvedParameters.filter(isParameterWithoutIn).reduce( - (acc, parameter: ParameterWithoutIn) => { - const ctxWithInputs = { - ...ctx, - $inputs: { - ...ctx.$inputs, - ...(workflowId ? ctx.$workflows[workflowId]?.inputs : {}), - }, - }; - // Ensure parameter is of type ParameterWithoutIn - acc[parameter.name] = getValueFromContext({ - value: parameter.value, - ctx: ctxWithInputs, - logger: ctx.options.logger, - }); - return acc; - }, - {} as Record - ); + const workflowInputParameters = mapParametersToWorkflowInputs({ + parameters: resolvedParameters, + ctx, + workflowId, + }); // Merge the runtime inputs with the inputs passed in the step as parameters for the workflow workflowCtx.$workflows[targetWorkflow.workflowId].inputs = { @@ -304,11 +316,12 @@ export async function runStep({ if (matchesCriteria) { const targetWorkflow = action.workflowId - ? (getValueFromContext({ + ? ctx.workflows.find((w) => w.workflowId === action.workflowId) || + (getValueFromContext({ value: action.workflowId, ctx, logger: ctx.options.logger, - }) as Workflow) + }) as Workflow | undefined) : undefined; const targetCtx = action.workflowId && targetWorkflow @@ -320,6 +333,25 @@ export async function runStep({ ) : { ...ctx, executedSteps: [] }; + const targetWorkflowInputs = + targetWorkflow?.workflowId && targetCtx.$workflows[targetWorkflow.workflowId]; + if (targetWorkflowInputs && action.parameters?.length) { + // The action parameters map to the inputs of the workflow referenced by the action's workflowId. + const resolvedActionParameters = action.parameters.map( + (parameter) => resolveReusableComponentItem(parameter, ctx) as ResolvedParameter + ); + const workflowInputParameters = mapParametersToWorkflowInputs({ + parameters: resolvedActionParameters, + ctx, + workflowId, + }); + + targetWorkflowInputs.inputs = { + ...targetWorkflowInputs.inputs, + ...workflowInputParameters, + }; + } + const targetStep = action.stepId ? action.stepId : undefined; if (type === 'retry') { From 50a990eb267e3f50aab03ca5a28e8ac667e69c1f Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 28 May 2026 16:34:23 +0530 Subject: [PATCH 15/26] feat: added mapParametersToWorkflowInput tests --- .../__tests__/flow-runner/run-step.test.ts | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/packages/respect-core/src/modules/__tests__/flow-runner/run-step.test.ts b/packages/respect-core/src/modules/__tests__/flow-runner/run-step.test.ts index 89189658f4..8076beed92 100644 --- a/packages/respect-core/src/modules/__tests__/flow-runner/run-step.test.ts +++ b/packages/respect-core/src/modules/__tests__/flow-runner/run-step.test.ts @@ -1113,6 +1113,108 @@ describe('runStep', () => { expect(runWorkflow).toHaveBeenCalled(); }); + it('should map onSuccess action parameters to the target workflow inputs', async () => { + const stepOne: Step = { + stepId: 'get-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + successCriteria: [{ condition: '$statusCode == 200' }], + onSuccess: [ + { + name: 'success-action', + workflowId: 'success-action-workflow', + type: 'goto', + parameters: [{ name: 'birdId', value: 'abc-123' }], + criteria: [{ condition: '$statusCode == 200' }], + }, + ], + checks: [], + response: {} as any, + }; + const workflowId = 'get-bird-workflow'; + + vi.mocked(callAPIAndAnalyzeResults).mockImplementationOnce(async ({ step }: { step: Step }) => { + step.checks = [ + { + name: CHECKS.STATUS_CODE_CHECK, + passed: true, + message: '', + severity: 'error', + }, + ]; + + return { + successCriteriaCheck: true, + schemaCheck: true, + networkCheck: true, + unexpectedErrorCheck: true, + statusCodeCheck: true, + }; + }); + + vi.mocked(checkCriteria).mockImplementation(() => [ + { + name: CHECKS.SUCCESS_CRITERIA_CHECK, + passed: true, + message: 'Checking simple criteria: {"condition":"$statusCode == 200"}', + severity: 'error', + }, + ]); + + const context = { + ...basicCTX, + $workflows: { + 'get-bird-workflow': { steps: {}, inputs: {} }, + 'success-action-workflow': { steps: {}, inputs: {} }, + }, + workflows: [ + { + workflowId: 'get-bird-workflow', + steps: [stepOne], + }, + { + workflowId: 'success-action-workflow', + steps: [ + { + stepId: 'use-bird', + 'x-operation': { + url: 'http://localhost:3000/bird', + method: 'get', + }, + checks: [], + }, + ], + }, + ], + } as unknown as TestContext; + + let capturedInputs: Record | undefined; + vi.mocked(runWorkflow).mockImplementationOnce(async ({ ctx }) => { + capturedInputs = ctx.$workflows['success-action-workflow']?.inputs; + return { + type: 'workflow', + invocationContext: {}, + workflowId, + } as WorkflowExecutionResult; + }); + + vi.mocked(resolveWorkflowContext).mockImplementationOnce(async () => { + return { ...context, executedSteps: [] }; + }); + + await runStep({ + step: stepOne, + ctx: context, + workflowId, + executedStepsCount: { value: 0 }, + }); + + expect(runWorkflow).toHaveBeenCalled(); + expect(capturedInputs).toEqual({ birdId: 'abc-123' }); + }); + it('should log error when onSuccess step criteria with goto StepId and WorkflowId provided', async () => { const stepOne: Step = { stepId: 'get-bird', From f7deff38a9ca29e4e5e4f06f29581a0747838bf4 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 28 May 2026 18:08:31 +0530 Subject: [PATCH 16/26] fix: address cursor comments --- .../__tests__/parameters-unique.test.ts | 83 +++++++++++++++++++ .../src/rules/arazzo/parameters-unique.ts | 51 +++++++----- 2 files changed, 113 insertions(+), 21 deletions(-) diff --git a/packages/core/src/rules/arazzo/__tests__/parameters-unique.test.ts b/packages/core/src/rules/arazzo/__tests__/parameters-unique.test.ts index 9722cc776c..c5f342d66f 100644 --- a/packages/core/src/rules/arazzo/__tests__/parameters-unique.test.ts +++ b/packages/core/src/rules/arazzo/__tests__/parameters-unique.test.ts @@ -112,4 +112,87 @@ describe('Arazzo parameters-unique', () => { ] `); }); + + it('should report on duplicated `parameters` defined on success/failure actions', async () => { + const actionDocument = parseYamlToDocument( + outdent` + arazzo: '1.0.1' + info: + title: Cool API + version: 1.0.0 + description: A cool API + sourceDescriptions: + - name: museum-api + type: openapi + url: openapi.yaml + workflows: + - workflowId: outer + steps: + - stepId: step-1 + operationId: museum-api.getMuseumHours + onSuccess: + - name: go-next + type: goto + workflowId: inner + parameters: + - name: token + value: a + - name: token + value: b + onFailure: + - name: recover + type: goto + workflowId: inner + parameters: + - name: retryToken + value: a + - name: retryToken + value: b + - workflowId: inner + steps: + - stepId: noop + operationId: museum-api.getMuseumHours + `, + 'arazzo.yaml' + ); + + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document: actionDocument, + config: await createConfig({ + rules: { 'parameters-unique': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(` + [ + { + "location": [ + { + "pointer": "#/workflows/0/steps/0/onSuccess/0/parameters/1", + "reportOnKey": false, + "source": "arazzo.yaml", + }, + ], + "message": "The parameter \`name\` must be unique amongst listed parameters.", + "ruleId": "parameters-unique", + "severity": "error", + "suggest": [], + }, + { + "location": [ + { + "pointer": "#/workflows/0/steps/0/onFailure/0/parameters/1", + "reportOnKey": false, + "source": "arazzo.yaml", + }, + ], + "message": "The parameter \`name\` must be unique amongst listed parameters.", + "ruleId": "parameters-unique", + "severity": "error", + "suggest": [], + }, + ] + `); + }); }); diff --git a/packages/core/src/rules/arazzo/parameters-unique.ts b/packages/core/src/rules/arazzo/parameters-unique.ts index ca69a82cfb..8f617ca6e3 100644 --- a/packages/core/src/rules/arazzo/parameters-unique.ts +++ b/packages/core/src/rules/arazzo/parameters-unique.ts @@ -1,30 +1,39 @@ import type { Arazzo1Rule } from '../../visitors.js'; import type { UserContext } from '../../walk.js'; -export const ParametersUnique: Arazzo1Rule = () => { - return { - Parameters: { - enter(parameters, { report, location }: UserContext) { - if (!parameters) return; - const seenParameters = new Set(); +function checkParametersUnique(parameters: any, { report, location }: UserContext) { + if (!parameters) return; + const seenParameters = new Set(); - for (const parameter of parameters) { - if (seenParameters.has(parameter?.name)) { - report({ - message: 'The parameter `name` must be unique amongst listed parameters.', - location: location.child([parameters.indexOf(parameter)]), - }); - } + for (const parameter of parameters) { + if (seenParameters.has(parameter?.name)) { + report({ + message: 'The parameter `name` must be unique amongst listed parameters.', + location: location.child([parameters.indexOf(parameter)]), + }); + } - if (seenParameters.has(parameter?.reference)) { - report({ - message: 'The parameter `reference` must be unique amongst listed parameters.', - location: location.child([parameters.indexOf(parameter)]), - }); - } + if (seenParameters.has(parameter?.reference)) { + report({ + message: 'The parameter `reference` must be unique amongst listed parameters.', + location: location.child([parameters.indexOf(parameter)]), + }); + } - seenParameters.add(parameter?.name ?? parameter?.reference); - } + seenParameters.add(parameter?.name ?? parameter?.reference); + } +} + +export const ParametersUnique: Arazzo1Rule = () => { + return { + Parameters: { + enter(parameters, ctx: UserContext) { + checkParametersUnique(parameters, ctx); + }, + }, + ActionParameters: { + enter(parameters, ctx: UserContext) { + checkParametersUnique(parameters, ctx); }, }, }; From 03b68966777cc23e8c2e4ee043dc483842a9953f Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Fri, 29 May 2026 20:54:04 +0530 Subject: [PATCH 17/26] fix: address comments and fix linting errors --- .../__snapshots__/redocly-yaml.test.ts.snap | 47 +------------------ .../src/rules/arazzo/parameters-unique.ts | 3 +- packages/respect-core/src/arazzo-schema.ts | 5 ++ .../src/modules/flow-runner/run-step.ts | 33 +++++++------ 4 files changed, 27 insertions(+), 61 deletions(-) diff --git a/packages/core/src/__tests__/__snapshots__/redocly-yaml.test.ts.snap b/packages/core/src/__tests__/__snapshots__/redocly-yaml.test.ts.snap index 7e026eed64..2de71d8ac5 100644 --- a/packages/core/src/__tests__/__snapshots__/redocly-yaml.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/redocly-yaml.test.ts.snap @@ -1324,6 +1324,8 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] "OpenAPISourceDescription", "ArazzoSourceDescription", "Parameters", + "ActionParameters", + "ActionParameter", "ReusableObject", "Workflows", "Workflow", @@ -8329,13 +8331,7 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] "dismissible": { "type": "boolean", }, - "endAt": { - "type": "string", - }, "rbac": "rootRedoclyConfigSchema.banner_items.rbac", - "startAt": { - "type": "string", - }, "target": { "type": "string", }, @@ -16849,13 +16845,7 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] "dismissible": { "type": "boolean", }, - "endAt": { - "type": "string", - }, "rbac": "rootRedoclyConfigSchema.env_additionalProperties.banner_items.rbac", - "startAt": { - "type": "string", - }, "target": { "type": "string", }, @@ -19776,7 +19766,6 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] }, "type": "array", }, - "template": "rootRedoclyConfigSchema.env_additionalProperties.markdown.template", "toc": "rootRedoclyConfigSchema.env_additionalProperties.markdown.toc", }, "required": undefined, @@ -19820,16 +19809,6 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] }, "required": undefined, }, - "rootRedoclyConfigSchema.env_additionalProperties.markdown.template": { - "additionalProperties": { - "type": "string", - }, - "description": undefined, - "documentationLink": undefined, - "items": undefined, - "properties": {}, - "required": undefined, - }, "rootRedoclyConfigSchema.env_additionalProperties.markdown.toc": { "additionalProperties": undefined, "description": undefined, @@ -28115,7 +28094,6 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] }, "type": "array", }, - "template": "rootRedoclyConfigSchema.env_additionalProperties.theme.markdown.template", "toc": "rootRedoclyConfigSchema.env_additionalProperties.theme.markdown.toc", }, "required": undefined, @@ -28159,16 +28137,6 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] }, "required": undefined, }, - "rootRedoclyConfigSchema.env_additionalProperties.theme.markdown.template": { - "additionalProperties": { - "type": "string", - }, - "description": undefined, - "documentationLink": undefined, - "items": undefined, - "properties": {}, - "required": undefined, - }, "rootRedoclyConfigSchema.env_additionalProperties.theme.markdown.toc": { "additionalProperties": undefined, "description": undefined, @@ -33492,7 +33460,6 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] }, "type": "array", }, - "template": "rootRedoclyConfigSchema.markdown.template", "toc": "rootRedoclyConfigSchema.markdown.toc", }, "required": undefined, @@ -33536,16 +33503,6 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] }, "required": undefined, }, - "rootRedoclyConfigSchema.markdown.template": { - "additionalProperties": { - "type": "string", - }, - "description": undefined, - "documentationLink": undefined, - "items": undefined, - "properties": {}, - "required": undefined, - }, "rootRedoclyConfigSchema.markdown.toc": { "additionalProperties": undefined, "description": undefined, diff --git a/packages/core/src/rules/arazzo/parameters-unique.ts b/packages/core/src/rules/arazzo/parameters-unique.ts index 8f617ca6e3..283e1fae80 100644 --- a/packages/core/src/rules/arazzo/parameters-unique.ts +++ b/packages/core/src/rules/arazzo/parameters-unique.ts @@ -1,7 +1,8 @@ +import type { Parameter } from '../../typings/arazzo.js'; import type { Arazzo1Rule } from '../../visitors.js'; import type { UserContext } from '../../walk.js'; -function checkParametersUnique(parameters: any, { report, location }: UserContext) { +function checkParametersUnique(parameters: Parameter[], { report, location }: UserContext) { if (!parameters) return; const seenParameters = new Set(); diff --git a/packages/respect-core/src/arazzo-schema.ts b/packages/respect-core/src/arazzo-schema.ts index 7caeb9f214..7d6d30ae41 100644 --- a/packages/respect-core/src/arazzo-schema.ts +++ b/packages/respect-core/src/arazzo-schema.ts @@ -150,6 +150,7 @@ export const reusableObject = { required: ['reference'], additionalProperties: false, } as const; + export const parameter = { type: 'object', oneOf: [ @@ -169,10 +170,12 @@ export const parameter = { reusableObject, ], } as const; + const parameters = { type: 'array', items: parameter, } as const; + export const actionParameter = { type: 'object', oneOf: [ @@ -191,10 +194,12 @@ export const actionParameter = { reusableObject, ], } as const; + const actionParameters = { type: 'array', items: actionParameter, } as const; + export const infoObject = { type: 'object', properties: { diff --git a/packages/respect-core/src/modules/flow-runner/run-step.ts b/packages/respect-core/src/modules/flow-runner/run-step.ts index 71e726e410..405856f45a 100644 --- a/packages/respect-core/src/modules/flow-runner/run-step.ts +++ b/packages/respect-core/src/modules/flow-runner/run-step.ts @@ -42,21 +42,24 @@ function mapParametersToWorkflowInputs({ ctx: TestContext; workflowId: string | undefined; }): Record { - return parameters.filter(isParameterWithoutIn).reduce((acc, parameter: ParameterWithoutIn) => { - const ctxWithInputs = { - ...ctx, - $inputs: { - ...ctx.$inputs, - ...(workflowId ? ctx.$workflows[workflowId]?.inputs : {}), - }, - }; - acc[parameter.name] = getValueFromContext({ - value: parameter.value, - ctx: ctxWithInputs, - logger: ctx.options.logger, - }); - return acc; - }, {} as Record); + return parameters.filter(isParameterWithoutIn).reduce( + (acc, parameter: ParameterWithoutIn) => { + const ctxWithInputs = { + ...ctx, + $inputs: { + ...ctx.$inputs, + ...(workflowId ? ctx.$workflows[workflowId]?.inputs : {}), + }, + }; + acc[parameter.name] = getValueFromContext({ + value: parameter.value, + ctx: ctxWithInputs, + logger: ctx.options.logger, + }); + return acc; + }, + {} as Record + ); } export async function runStep({ From 1ff644fd7cf333eea138f989d0740f03b4a95ae8 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Fri, 29 May 2026 20:59:03 +0530 Subject: [PATCH 18/26] fix: linting errors --- .changeset/swift-otters-wander.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/swift-otters-wander.md b/.changeset/swift-otters-wander.md index 9d2c5299df..055d29a742 100644 --- a/.changeset/swift-otters-wander.md +++ b/.changeset/swift-otters-wander.md @@ -1,7 +1,7 @@ --- -"@redocly/openapi-core": minor -"@redocly/respect-core": minor -"@redocly/cli": minor +'@redocly/openapi-core': minor +'@redocly/respect-core': minor +'@redocly/cli': minor --- Added the `spec-parameters-in-by-context` Arazzo rule, which validates that a parameter's `in` field is specified when the parent workflow, step, success action, or failure action does not reference a `workflowId`. Extended success and failure action objects to accept a `parameters` property that maps to workflow inputs. From b12d4c7c4f9a06c4df57501c2b3e0f8e0e0486a7 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Fri, 29 May 2026 21:29:07 +0530 Subject: [PATCH 19/26] fix: failing build test --- .../__snapshots__/redocly-yaml.test.ts.snap | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/packages/core/src/__tests__/__snapshots__/redocly-yaml.test.ts.snap b/packages/core/src/__tests__/__snapshots__/redocly-yaml.test.ts.snap index 2de71d8ac5..555c0e9787 100644 --- a/packages/core/src/__tests__/__snapshots__/redocly-yaml.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/redocly-yaml.test.ts.snap @@ -8331,7 +8331,13 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] "dismissible": { "type": "boolean", }, + "endAt": { + "type": "string", + }, "rbac": "rootRedoclyConfigSchema.banner_items.rbac", + "startAt": { + "type": "string", + }, "target": { "type": "string", }, @@ -16845,7 +16851,13 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] "dismissible": { "type": "boolean", }, + "endAt": { + "type": "string", + }, "rbac": "rootRedoclyConfigSchema.env_additionalProperties.banner_items.rbac", + "startAt": { + "type": "string", + }, "target": { "type": "string", }, @@ -19766,6 +19778,7 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] }, "type": "array", }, + "template": "rootRedoclyConfigSchema.env_additionalProperties.markdown.template", "toc": "rootRedoclyConfigSchema.env_additionalProperties.markdown.toc", }, "required": undefined, @@ -19809,6 +19822,16 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] }, "required": undefined, }, + "rootRedoclyConfigSchema.env_additionalProperties.markdown.template": { + "additionalProperties": { + "type": "string", + }, + "description": undefined, + "documentationLink": undefined, + "items": undefined, + "properties": {}, + "required": undefined, + }, "rootRedoclyConfigSchema.env_additionalProperties.markdown.toc": { "additionalProperties": undefined, "description": undefined, @@ -28094,6 +28117,7 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] }, "type": "array", }, + "template": "rootRedoclyConfigSchema.env_additionalProperties.theme.markdown.template", "toc": "rootRedoclyConfigSchema.env_additionalProperties.theme.markdown.toc", }, "required": undefined, @@ -28137,6 +28161,16 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] }, "required": undefined, }, + "rootRedoclyConfigSchema.env_additionalProperties.theme.markdown.template": { + "additionalProperties": { + "type": "string", + }, + "description": undefined, + "documentationLink": undefined, + "items": undefined, + "properties": {}, + "required": undefined, + }, "rootRedoclyConfigSchema.env_additionalProperties.theme.markdown.toc": { "additionalProperties": undefined, "description": undefined, @@ -33460,6 +33494,7 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] }, "type": "array", }, + "template": "rootRedoclyConfigSchema.markdown.template", "toc": "rootRedoclyConfigSchema.markdown.toc", }, "required": undefined, @@ -33503,6 +33538,16 @@ exports[`createConfigTypes > matches snapshot for the default config schema 1`] }, "required": undefined, }, + "rootRedoclyConfigSchema.markdown.template": { + "additionalProperties": { + "type": "string", + }, + "description": undefined, + "documentationLink": undefined, + "items": undefined, + "properties": {}, + "required": undefined, + }, "rootRedoclyConfigSchema.markdown.toc": { "additionalProperties": undefined, "description": undefined, From a58b5071d1122dfca83f7e3db289efd22a17b4a9 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Tue, 2 Jun 2026 22:50:38 +0530 Subject: [PATCH 20/26] fix: failing lint test --- .../core/src/rules/arazzo/spec-parameters-in-by-context.ts | 4 +++- .../wrong-config-type-extensions-in-assertions/snapshot.txt | 2 +- .../invalid-config-assertation-config-type/snapshot.txt | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts index c64193259f..b1ced0f2b6 100644 --- a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts +++ b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts @@ -1,9 +1,11 @@ +import { isPlainObject } from '../../utils/is-plain-object.js'; + import type { Parameter } from '../../typings/arazzo.js'; import type { Arazzo1Rule } from '../../visitors.js'; import type { UserContext } from '../../walk.js'; function isInlineParameter(parameter: Parameter): boolean { - return Boolean(parameter) && typeof parameter === 'object' && !('reference' in parameter); + return isPlainObject(parameter) && !('reference' in parameter); } function checkParameters( diff --git a/tests/e2e/check-config/wrong-config-type-extensions-in-assertions/snapshot.txt b/tests/e2e/check-config/wrong-config-type-extensions-in-assertions/snapshot.txt index c440e889dc..87010d187f 100644 --- a/tests/e2e/check-config/wrong-config-type-extensions-in-assertions/snapshot.txt +++ b/tests/e2e/check-config/wrong-config-type-extensions-in-assertions/snapshot.txt @@ -1,6 +1,6 @@ [1] redocly.yaml:10:13 at #/rules/rule~1metadata-lifecycle/subject/type -`type` can be one of the following only: "any", "Root", "Tag", "TagList", "TagGroups", "TagGroup", "ExternalDocs", "Example", "ExamplesMap", "EnumDescriptions", "SecurityRequirement", "SecurityRequirementList", "Info", "Contact", "License", "Logo", "Paths", "PathItem", "Parameter", "ParameterItems", "ParameterList", "Operation", "Examples", "Header", "Responses", "Response", "Schema", "Xml", "SchemaProperties", "NamedSchemas", "NamedResponses", "NamedParameters", "NamedSecuritySchemes", "SecurityScheme", "XCodeSample", "XCodeSampleList", "XServerList", "XServer", "Server", "ServerList", "ServerVariable", "ServerVariablesMap", "Callback", "CallbacksMap", "RequestBody", "MediaTypesMap", "MediaType", "Encoding", "EncodingMap", "HeadersMap", "Link", "DiscriminatorMapping", "Discriminator", "Components", "LinksMap", "NamedExamples", "NamedRequestBodies", "NamedHeaders", "NamedLinks", "NamedCallbacks", "ImplicitFlow", "PasswordFlow", "ClientCredentials", "AuthorizationCode", "OAuth2Flows", "XUsePkce", "WebhooksMap", "XMetaData", "PatternProperties", "NamedPathItems", "DependentRequired", "DeviceAuthorization", "NamedMediaTypes", "HttpServerBinding", "HttpChannelBinding", "HttpMessageBinding", "HttpOperationBinding", "WsServerBinding", "WsChannelBinding", "WsMessageBinding", "WsOperationBinding", "KafkaServerBinding", "KafkaTopicConfiguration", "KafkaChannelBinding", "KafkaMessageBinding", "KafkaOperationBinding", "AnypointmqServerBinding", "AnypointmqChannelBinding", "AnypointmqMessageBinding", "AnypointmqOperationBinding", "AmqpServerBinding", "AmqpChannelBinding", "AmqpMessageBinding", "AmqpOperationBinding", "Amqp1ServerBinding", "Amqp1ChannelBinding", "Amqp1MessageBinding", "Amqp1OperationBinding", "MqttServerBindingLastWill", "MqttServerBinding", "MqttChannelBinding", "MqttMessageBinding", "MqttOperationBinding", "Mqtt5ServerBinding", "Mqtt5ChannelBinding", "Mqtt5MessageBinding", "Mqtt5OperationBinding", "NatsServerBinding", "NatsChannelBinding", "NatsMessageBinding", "NatsOperationBinding", "JmsServerBinding", "JmsChannelBinding", "JmsMessageBinding", "JmsOperationBinding", "SolaceServerBinding", "SolaceChannelBinding", "SolaceMessageBinding", "SolaceDestination", "SolaceOperationBinding", "StompServerBinding", "StompChannelBinding", "StompMessageBinding", "StompOperationBinding", "RedisServerBinding", "RedisChannelBinding", "RedisMessageBinding", "RedisOperationBinding", "MercureServerBinding", "MercureChannelBinding", "MercureMessageBinding", "MercureOperationBinding", "ServerBindings", "ChannelBindings", "MessageBindings", "OperationBindings", "ServerMap", "ChannelMap", "Channel", "ParametersMap", "MessageExample", "NamedMessages", "NamedMessageTraits", "NamedOperationTraits", "NamedCorrelationIds", "SecuritySchemeFlows", "Message", "OperationTrait", "OperationTraitList", "MessageTrait", "MessageTraitList", "MessageExampleList", "CorrelationId", "Dependencies", "OperationReply", "OperationReplyAddress", "NamedTags", "NamedExternalDocs", "NamedChannels", "NamedOperations", "NamedOperationReplies", "NamedOperationRelyAddresses", "SecuritySchemeList", "MessageList", "SourceDescriptions", "OpenAPISourceDescription", "ArazzoSourceDescription", "Parameters", "ReusableObject", "Workflows", "Workflow", "Steps", "Step", "Replacement", "ExtendedOperation", "ExtendedSecurityList", "ExtendedSecurity", "Outputs", "CriterionObject", "XPathCriterion", "JSONPathCriterion", "SuccessActionObject", "OnSuccessActionList", "FailureActionObject", "OnFailureActionList", "NamedInputs", "NamedSuccessActions", "NamedFailureActions", "Actions", "Action", "Method", "MethodList", "ContentDescriptor", "ContentDescriptorList", "ExamplePairing", "ExamplePairingList", "ExampleList", "LinkList", "ErrorObject", "ErrorList", "NamedContentDescriptors", "NamedErrors", "NamedExamplePairingObjects", "SpecExtension". +`type` can be one of the following only: "any", "Root", "Tag", "TagList", "TagGroups", "TagGroup", "ExternalDocs", "Example", "ExamplesMap", "EnumDescriptions", "SecurityRequirement", "SecurityRequirementList", "Info", "Contact", "License", "Logo", "Paths", "PathItem", "Parameter", "ParameterItems", "ParameterList", "Operation", "Examples", "Header", "Responses", "Response", "Schema", "Xml", "SchemaProperties", "NamedSchemas", "NamedResponses", "NamedParameters", "NamedSecuritySchemes", "SecurityScheme", "XCodeSample", "XCodeSampleList", "XServerList", "XServer", "Server", "ServerList", "ServerVariable", "ServerVariablesMap", "Callback", "CallbacksMap", "RequestBody", "MediaTypesMap", "MediaType", "Encoding", "EncodingMap", "HeadersMap", "Link", "DiscriminatorMapping", "Discriminator", "Components", "LinksMap", "NamedExamples", "NamedRequestBodies", "NamedHeaders", "NamedLinks", "NamedCallbacks", "ImplicitFlow", "PasswordFlow", "ClientCredentials", "AuthorizationCode", "OAuth2Flows", "XUsePkce", "WebhooksMap", "XMetaData", "PatternProperties", "NamedPathItems", "DependentRequired", "DeviceAuthorization", "NamedMediaTypes", "HttpServerBinding", "HttpChannelBinding", "HttpMessageBinding", "HttpOperationBinding", "WsServerBinding", "WsChannelBinding", "WsMessageBinding", "WsOperationBinding", "KafkaServerBinding", "KafkaTopicConfiguration", "KafkaChannelBinding", "KafkaMessageBinding", "KafkaOperationBinding", "AnypointmqServerBinding", "AnypointmqChannelBinding", "AnypointmqMessageBinding", "AnypointmqOperationBinding", "AmqpServerBinding", "AmqpChannelBinding", "AmqpMessageBinding", "AmqpOperationBinding", "Amqp1ServerBinding", "Amqp1ChannelBinding", "Amqp1MessageBinding", "Amqp1OperationBinding", "MqttServerBindingLastWill", "MqttServerBinding", "MqttChannelBinding", "MqttMessageBinding", "MqttOperationBinding", "Mqtt5ServerBinding", "Mqtt5ChannelBinding", "Mqtt5MessageBinding", "Mqtt5OperationBinding", "NatsServerBinding", "NatsChannelBinding", "NatsMessageBinding", "NatsOperationBinding", "JmsServerBinding", "JmsChannelBinding", "JmsMessageBinding", "JmsOperationBinding", "SolaceServerBinding", "SolaceChannelBinding", "SolaceMessageBinding", "SolaceDestination", "SolaceOperationBinding", "StompServerBinding", "StompChannelBinding", "StompMessageBinding", "StompOperationBinding", "RedisServerBinding", "RedisChannelBinding", "RedisMessageBinding", "RedisOperationBinding", "MercureServerBinding", "MercureChannelBinding", "MercureMessageBinding", "MercureOperationBinding", "ServerBindings", "ChannelBindings", "MessageBindings", "OperationBindings", "ServerMap", "ChannelMap", "Channel", "ParametersMap", "MessageExample", "NamedMessages", "NamedMessageTraits", "NamedOperationTraits", "NamedCorrelationIds", "SecuritySchemeFlows", "Message", "OperationTrait", "OperationTraitList", "MessageTrait", "MessageTraitList", "MessageExampleList", "CorrelationId", "Dependencies", "OperationReply", "OperationReplyAddress", "NamedTags", "NamedExternalDocs", "NamedChannels", "NamedOperations", "NamedOperationReplies", "NamedOperationRelyAddresses", "SecuritySchemeList", "MessageList", "SourceDescriptions", "OpenAPISourceDescription", "ArazzoSourceDescription", "Parameters", "ActionParameters", "ActionParameter", "ReusableObject", "Workflows", "Workflow", "Steps", "Step", "Replacement", "ExtendedOperation", "ExtendedSecurityList", "ExtendedSecurity", "Outputs", "CriterionObject", "XPathCriterion", "JSONPathCriterion", "SuccessActionObject", "OnSuccessActionList", "FailureActionObject", "OnFailureActionList", "NamedInputs", "NamedSuccessActions", "NamedFailureActions", "Actions", "Action", "Method", "MethodList", "ContentDescriptor", "ContentDescriptorList", "ExamplePairing", "ExamplePairingList", "ExampleList", "LinkList", "ErrorObject", "ErrorList", "NamedContentDescriptors", "NamedErrors", "NamedExamplePairingObjects", "SpecExtension". 8 | rule/metadata-lifecycle: 9 | subject: diff --git a/tests/e2e/lint-config/invalid-config-assertation-config-type/snapshot.txt b/tests/e2e/lint-config/invalid-config-assertation-config-type/snapshot.txt index ec4778d4ff..0f20b4ea6e 100644 --- a/tests/e2e/lint-config/invalid-config-assertation-config-type/snapshot.txt +++ b/tests/e2e/lint-config/invalid-config-assertation-config-type/snapshot.txt @@ -1,6 +1,6 @@ [1] redocly.yaml:5:17 at #/rules/rule~1path-item-mutually-required/where/0/subject/type -`type` can be one of the following only: "any", "Root", "Tag", "TagList", "TagGroups", "TagGroup", "ExternalDocs", "Example", "ExamplesMap", "EnumDescriptions", "SecurityRequirement", "SecurityRequirementList", "Info", "Contact", "License", "Logo", "Paths", "PathItem", "Parameter", "ParameterItems", "ParameterList", "Operation", "Examples", "Header", "Responses", "Response", "Schema", "Xml", "SchemaProperties", "NamedSchemas", "NamedResponses", "NamedParameters", "NamedSecuritySchemes", "SecurityScheme", "XCodeSample", "XCodeSampleList", "XServerList", "XServer", "Server", "ServerList", "ServerVariable", "ServerVariablesMap", "Callback", "CallbacksMap", "RequestBody", "MediaTypesMap", "MediaType", "Encoding", "EncodingMap", "HeadersMap", "Link", "DiscriminatorMapping", "Discriminator", "Components", "LinksMap", "NamedExamples", "NamedRequestBodies", "NamedHeaders", "NamedLinks", "NamedCallbacks", "ImplicitFlow", "PasswordFlow", "ClientCredentials", "AuthorizationCode", "OAuth2Flows", "XUsePkce", "WebhooksMap", "PatternProperties", "NamedPathItems", "DependentRequired", "DeviceAuthorization", "NamedMediaTypes", "HttpServerBinding", "HttpChannelBinding", "HttpMessageBinding", "HttpOperationBinding", "WsServerBinding", "WsChannelBinding", "WsMessageBinding", "WsOperationBinding", "KafkaServerBinding", "KafkaTopicConfiguration", "KafkaChannelBinding", "KafkaMessageBinding", "KafkaOperationBinding", "AnypointmqServerBinding", "AnypointmqChannelBinding", "AnypointmqMessageBinding", "AnypointmqOperationBinding", "AmqpServerBinding", "AmqpChannelBinding", "AmqpMessageBinding", "AmqpOperationBinding", "Amqp1ServerBinding", "Amqp1ChannelBinding", "Amqp1MessageBinding", "Amqp1OperationBinding", "MqttServerBindingLastWill", "MqttServerBinding", "MqttChannelBinding", "MqttMessageBinding", "MqttOperationBinding", "Mqtt5ServerBinding", "Mqtt5ChannelBinding", "Mqtt5MessageBinding", "Mqtt5OperationBinding", "NatsServerBinding", "NatsChannelBinding", "NatsMessageBinding", "NatsOperationBinding", "JmsServerBinding", "JmsChannelBinding", "JmsMessageBinding", "JmsOperationBinding", "SolaceServerBinding", "SolaceChannelBinding", "SolaceMessageBinding", "SolaceDestination", "SolaceOperationBinding", "StompServerBinding", "StompChannelBinding", "StompMessageBinding", "StompOperationBinding", "RedisServerBinding", "RedisChannelBinding", "RedisMessageBinding", "RedisOperationBinding", "MercureServerBinding", "MercureChannelBinding", "MercureMessageBinding", "MercureOperationBinding", "ServerBindings", "ChannelBindings", "MessageBindings", "OperationBindings", "ServerMap", "ChannelMap", "Channel", "ParametersMap", "MessageExample", "NamedMessages", "NamedMessageTraits", "NamedOperationTraits", "NamedCorrelationIds", "SecuritySchemeFlows", "Message", "OperationTrait", "OperationTraitList", "MessageTrait", "MessageTraitList", "MessageExampleList", "CorrelationId", "Dependencies", "OperationReply", "OperationReplyAddress", "NamedTags", "NamedExternalDocs", "NamedChannels", "NamedOperations", "NamedOperationReplies", "NamedOperationRelyAddresses", "SecuritySchemeList", "MessageList", "SourceDescriptions", "OpenAPISourceDescription", "ArazzoSourceDescription", "Parameters", "ReusableObject", "Workflows", "Workflow", "Steps", "Step", "Replacement", "ExtendedOperation", "ExtendedSecurityList", "ExtendedSecurity", "Outputs", "CriterionObject", "XPathCriterion", "JSONPathCriterion", "SuccessActionObject", "OnSuccessActionList", "FailureActionObject", "OnFailureActionList", "NamedInputs", "NamedSuccessActions", "NamedFailureActions", "Actions", "Action", "Method", "MethodList", "ContentDescriptor", "ContentDescriptorList", "ExamplePairing", "ExamplePairingList", "ExampleList", "LinkList", "ErrorObject", "ErrorList", "NamedContentDescriptors", "NamedErrors", "NamedExamplePairingObjects", "SpecExtension". +`type` can be one of the following only: "any", "Root", "Tag", "TagList", "TagGroups", "TagGroup", "ExternalDocs", "Example", "ExamplesMap", "EnumDescriptions", "SecurityRequirement", "SecurityRequirementList", "Info", "Contact", "License", "Logo", "Paths", "PathItem", "Parameter", "ParameterItems", "ParameterList", "Operation", "Examples", "Header", "Responses", "Response", "Schema", "Xml", "SchemaProperties", "NamedSchemas", "NamedResponses", "NamedParameters", "NamedSecuritySchemes", "SecurityScheme", "XCodeSample", "XCodeSampleList", "XServerList", "XServer", "Server", "ServerList", "ServerVariable", "ServerVariablesMap", "Callback", "CallbacksMap", "RequestBody", "MediaTypesMap", "MediaType", "Encoding", "EncodingMap", "HeadersMap", "Link", "DiscriminatorMapping", "Discriminator", "Components", "LinksMap", "NamedExamples", "NamedRequestBodies", "NamedHeaders", "NamedLinks", "NamedCallbacks", "ImplicitFlow", "PasswordFlow", "ClientCredentials", "AuthorizationCode", "OAuth2Flows", "XUsePkce", "WebhooksMap", "PatternProperties", "NamedPathItems", "DependentRequired", "DeviceAuthorization", "NamedMediaTypes", "HttpServerBinding", "HttpChannelBinding", "HttpMessageBinding", "HttpOperationBinding", "WsServerBinding", "WsChannelBinding", "WsMessageBinding", "WsOperationBinding", "KafkaServerBinding", "KafkaTopicConfiguration", "KafkaChannelBinding", "KafkaMessageBinding", "KafkaOperationBinding", "AnypointmqServerBinding", "AnypointmqChannelBinding", "AnypointmqMessageBinding", "AnypointmqOperationBinding", "AmqpServerBinding", "AmqpChannelBinding", "AmqpMessageBinding", "AmqpOperationBinding", "Amqp1ServerBinding", "Amqp1ChannelBinding", "Amqp1MessageBinding", "Amqp1OperationBinding", "MqttServerBindingLastWill", "MqttServerBinding", "MqttChannelBinding", "MqttMessageBinding", "MqttOperationBinding", "Mqtt5ServerBinding", "Mqtt5ChannelBinding", "Mqtt5MessageBinding", "Mqtt5OperationBinding", "NatsServerBinding", "NatsChannelBinding", "NatsMessageBinding", "NatsOperationBinding", "JmsServerBinding", "JmsChannelBinding", "JmsMessageBinding", "JmsOperationBinding", "SolaceServerBinding", "SolaceChannelBinding", "SolaceMessageBinding", "SolaceDestination", "SolaceOperationBinding", "StompServerBinding", "StompChannelBinding", "StompMessageBinding", "StompOperationBinding", "RedisServerBinding", "RedisChannelBinding", "RedisMessageBinding", "RedisOperationBinding", "MercureServerBinding", "MercureChannelBinding", "MercureMessageBinding", "MercureOperationBinding", "ServerBindings", "ChannelBindings", "MessageBindings", "OperationBindings", "ServerMap", "ChannelMap", "Channel", "ParametersMap", "MessageExample", "NamedMessages", "NamedMessageTraits", "NamedOperationTraits", "NamedCorrelationIds", "SecuritySchemeFlows", "Message", "OperationTrait", "OperationTraitList", "MessageTrait", "MessageTraitList", "MessageExampleList", "CorrelationId", "Dependencies", "OperationReply", "OperationReplyAddress", "NamedTags", "NamedExternalDocs", "NamedChannels", "NamedOperations", "NamedOperationReplies", "NamedOperationRelyAddresses", "SecuritySchemeList", "MessageList", "SourceDescriptions", "OpenAPISourceDescription", "ArazzoSourceDescription", "Parameters", "ActionParameters", "ActionParameter", "ReusableObject", "Workflows", "Workflow", "Steps", "Step", "Replacement", "ExtendedOperation", "ExtendedSecurityList", "ExtendedSecurity", "Outputs", "CriterionObject", "XPathCriterion", "JSONPathCriterion", "SuccessActionObject", "OnSuccessActionList", "FailureActionObject", "OnFailureActionList", "NamedInputs", "NamedSuccessActions", "NamedFailureActions", "Actions", "Action", "Method", "MethodList", "ContentDescriptor", "ContentDescriptorList", "ExamplePairing", "ExamplePairingList", "ExampleList", "LinkList", "ErrorObject", "ErrorList", "NamedContentDescriptors", "NamedErrors", "NamedExamplePairingObjects", "SpecExtension". 3 | where: 4 | - subject: From 41b854656f02a1db6948125ae89851b1c36017af Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Tue, 2 Jun 2026 23:09:04 +0530 Subject: [PATCH 21/26] fix: failing lint test --- .../core/src/rules/arazzo/spec-parameters-in-by-context.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts index b1ced0f2b6..50eecbcc9a 100644 --- a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts +++ b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts @@ -1,6 +1,5 @@ -import { isPlainObject } from '../../utils/is-plain-object.js'; - import type { Parameter } from '../../typings/arazzo.js'; +import { isPlainObject } from '../../utils/is-plain-object.js'; import type { Arazzo1Rule } from '../../visitors.js'; import type { UserContext } from '../../walk.js'; From ce4f733686f2d5ca6363f62f94e9680ca78f33e1 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 4 Jun 2026 16:04:46 +0530 Subject: [PATCH 22/26] fix: remove x-allowedReserved from actionParameter --- packages/respect-core/src/arazzo-schema.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/respect-core/src/arazzo-schema.ts b/packages/respect-core/src/arazzo-schema.ts index 7d6d30ae41..e2ec1eb0c6 100644 --- a/packages/respect-core/src/arazzo-schema.ts +++ b/packages/respect-core/src/arazzo-schema.ts @@ -186,7 +186,6 @@ export const actionParameter = { value: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - 'x-allowReserved': { type: 'boolean' }, }, required: ['name', 'value'], additionalProperties: false, From a14a54ba7c346afcec9a03f6e04760f25de88a8f Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 4 Jun 2026 16:18:28 +0530 Subject: [PATCH 23/26] fix: updated changesets --- .changeset/swift-otters-wander.md | 5 +---- .changeset/thin-aliens-accept.md | 7 +++++++ 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 .changeset/thin-aliens-accept.md diff --git a/.changeset/swift-otters-wander.md b/.changeset/swift-otters-wander.md index 055d29a742..96e58d1f27 100644 --- a/.changeset/swift-otters-wander.md +++ b/.changeset/swift-otters-wander.md @@ -1,9 +1,6 @@ --- '@redocly/openapi-core': minor -'@redocly/respect-core': minor '@redocly/cli': minor --- -Added the `spec-parameters-in-by-context` Arazzo rule, which validates that a parameter's `in` field is specified when the parent workflow, step, success action, or failure action does not reference a `workflowId`. Extended success and failure action objects to accept a `parameters` property that maps to workflow inputs. - -Note: because this rule is part of the `spec` ruleset (and is set to `error` in `recommended-strict` and `all`), linting Arazzo descriptions that omit a required `in` field, or that specify `in` when referencing a `workflowId`, may now report new errors. +Added the `spec-parameters-in-by-context` Arazzo rule, which validates that a parameter's `in` field is specified when the parent workflow, step, success action, or failure action does not reference a `workflowId`. diff --git a/.changeset/thin-aliens-accept.md b/.changeset/thin-aliens-accept.md new file mode 100644 index 0000000000..2bf346aee3 --- /dev/null +++ b/.changeset/thin-aliens-accept.md @@ -0,0 +1,7 @@ +--- +'@redocly/openapi-core': minor +'@redocly/respect-core': minor +'@redocly/cli': minor +--- + +Extended success and failure action objects to accept a `parameters` property that maps to workflow inputs. From a1443a6bbf1e8bce5d4617b808beb9a0c741c592 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 4 Jun 2026 16:23:35 +0530 Subject: [PATCH 24/26] fix: updated validation for in field in parameters --- .../arazzo/spec-parameters-in-by-context.md | 29 ++++++------------- .../arazzo/spec-parameters-in-by-context.ts | 21 ++++---------- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md b/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md index 6001616cbc..136fee1622 100644 --- a/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md +++ b/docs/@v2/rules/arazzo/spec-parameters-in-by-context.md @@ -1,6 +1,6 @@ # spec-parameters-in-by-context -Requires the `in` field on a parameter to be specified or omitted based on the parent context. +Validates how the `in` field is used on parameters based on the parent context. | Arazzo | Compatibility | | ------ | ------------- | @@ -8,10 +8,14 @@ Requires the `in` field on a parameter to be specified or omitted based on the p ## Design principles -The Arazzo specification states that when a step, success action, or failure action specifies a `workflowId`, all parameters map to the referenced workflow's inputs and the `in` field MUST NOT be specified. -In every other case (for example, when a step specifies an `operationId`, `operationPath`, or `x-operation`, or for parameters defined at the workflow level), the `in` field MUST be specified on each parameter. +The `in` field on an Arazzo parameter is not a required property — omitting it carries semantics. +When a step references a `workflowId`, a parameter with no `in` field is mapped to the referenced workflow's inputs. +When `in` is specified, the parameter is sent at that request location (`header`, `query`, `path`, or `cookie`) against the targeted operation. -This rule additionally enforces that success and failure action `parameters` are only valid when the action references a `workflowId`. +This rule enforces the following: + +- For a step that does not reference a `workflowId` (for example, one using `operationId`, `operationPath`, or `x-operation`), and for parameters defined at the workflow level, `in` must be specified on each inline parameter. +- Parameters on success and failure actions are only valid when the action references a `workflowId` — these parameters map to the referenced workflow's inputs and the spec states that `in` MUST NOT be used on them (see the [Success Action Object](https://spec.openapis.org/arazzo/latest.html#success-action-object) and [Failure Action Object](https://spec.openapis.org/arazzo/latest.html#failure-action-object)). ## Configuration @@ -50,7 +54,7 @@ workflows: value: '2024-01-01' ``` -Example of a **correct** step referencing a `workflowId` (parameters map to workflow inputs, no `in` field): +Example of a **correct** step referencing a `workflowId` (parameters omit `in` and are mapped to the referenced workflow's inputs): ```yaml # Correct example - workflowId @@ -82,21 +86,6 @@ workflows: value: '2024-01-01' ``` -Example of an **incorrect** step referencing a `workflowId` while declaring `in` on a parameter: - -```yaml -# Incorrect example - workflowId with `in` -workflows: - - workflowId: buy-tickets - steps: - - stepId: reuse-hours-workflow - workflowId: get-museum-hours - parameters: - - in: query - name: startDate - value: '2024-01-01' -``` - Example of an **incorrect** step referencing an `operationId` while omitting `in`: ```yaml diff --git a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts index 50eecbcc9a..b119c2d11c 100644 --- a/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts +++ b/packages/core/src/rules/arazzo/spec-parameters-in-by-context.ts @@ -7,26 +7,14 @@ function isInlineParameter(parameter: Parameter): boolean { return isPlainObject(parameter) && !('reference' in parameter); } -function checkParameters( - parameters: Parameter[], - hasWorkflowId: boolean, - { report, location }: UserContext -) { +function checkInRequired(parameters: Parameter[], { report, location }: UserContext) { if (!Array.isArray(parameters)) return; for (let i = 0; i < parameters.length; i++) { const parameter = parameters[i]; if (!isInlineParameter(parameter)) continue; - const hasIn = 'in' in parameter; - - if (hasWorkflowId && hasIn) { - report({ - message: - 'Parameter `in` field MUST NOT be specified when the parent references a `workflowId`; parameters map to workflow inputs.', - location: location.child(['parameters', i, 'in']).key(), - }); - } else if (!hasWorkflowId && !hasIn) { + if (!('in' in parameter)) { report({ message: 'Parameter `in` field MUST be specified when the parent does not reference a `workflowId`.', @@ -42,13 +30,14 @@ export const SpecParametersInByContext: Arazzo1Rule = () => { enter(workflow, ctx: UserContext) { if (!workflow.parameters) return; // A workflow never references another workflow, so `in` is always required. - checkParameters(workflow.parameters, false, ctx); + checkInRequired(workflow.parameters, ctx); }, }, Step: { enter(step, ctx: UserContext) { if (!step.parameters) return; - checkParameters(step.parameters, Boolean(step.workflowId), ctx); + if (step.workflowId) return; + checkInRequired(step.parameters, ctx); }, }, SuccessActionObject: { From 64f339e56f85d1db07c49b2fc48a0b74ebf12b69 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 4 Jun 2026 16:27:16 +0530 Subject: [PATCH 25/26] fix: updated tests for in field now --- .../spec-parameters-in-by-context.test.ts | 25 ++----------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts b/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts index c7bd14a82e..9a360d9839 100644 --- a/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts +++ b/packages/core/src/rules/arazzo/__tests__/spec-parameters-in-by-context.test.ts @@ -91,7 +91,7 @@ describe('Arazzo spec-parameters-in-by-context', () => { `); }); - it('should report when step references workflowId and a parameter has `in`', async () => { + it('should not report when step references workflowId and a parameter declares `in` (spec is silent on steps)', async () => { const document = parseYamlToDocument( outdent` arazzo: '1.0.1' @@ -104,11 +104,6 @@ describe('Arazzo spec-parameters-in-by-context', () => { url: openapi.yaml workflows: - workflowId: outer - inputs: - type: object - properties: - token: - type: string steps: - stepId: call-inner workflowId: inner @@ -132,23 +127,7 @@ describe('Arazzo spec-parameters-in-by-context', () => { }), }); - expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(` - [ - { - "location": [ - { - "pointer": "#/workflows/0/steps/0/parameters/0/in", - "reportOnKey": true, - "source": "arazzo.yaml", - }, - ], - "message": "Parameter \`in\` field MUST NOT be specified when the parent references a \`workflowId\`; parameters map to workflow inputs.", - "ruleId": "spec-parameters-in-by-context", - "severity": "error", - "suggest": [], - }, - ] - `); + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`); }); it('should not report when step references workflowId and parameter has no `in`', async () => { From ae9e4c7681a655d7120aa9731182741083216629 Mon Sep 17 00:00:00 2001 From: Harshit Singh Date: Thu, 4 Jun 2026 16:43:48 +0530 Subject: [PATCH 26/26] fix: failing e2e tests and added snapshots --- .../snapshot.txt | 528 +++++++++--------- .../build-docs/simple-build-docs/snapshot.txt | 2 +- .../snapshot.txt | 2 +- .../snapshot.txt | 2 +- .../x-security-bearer-auth.test.ts.snap | 58 +- .../x-security-oauth2-auth.test.ts.snap | 58 +- ...security-open-id-connect-auth.test.ts.snap | 58 +- 7 files changed, 282 insertions(+), 426 deletions(-) diff --git a/tests/e2e/build-docs/build-docs-with-disabled-search/snapshot.txt b/tests/e2e/build-docs/build-docs-with-disabled-search/snapshot.txt index 0a74414c31..e3f77a2337 100644 --- a/tests/e2e/build-docs/build-docs-with-disabled-search/snapshot.txt +++ b/tests/e2e/build-docs/build-docs-with-disabled-search/snapshot.txt @@ -12,273 +12,273 @@ margin: 0; } - -

Sample API (1.0.0)

Download OpenAPI specification:

Test.

-

Test

Responses

Response samples

Content type
application/json
{ }
+ " fill="currentColor">

Sample API (1.0.0)

Download OpenAPI specification:

Test.

+

Test

Responses

Response samples

Content type
application/json
{ }