diff --git a/.changeset/dull-pigs-jump.md b/.changeset/dull-pigs-jump.md new file mode 100644 index 0000000000..f7a5d34712 --- /dev/null +++ b/.changeset/dull-pigs-jump.md @@ -0,0 +1,6 @@ +--- +"@redocly/cli": patch +"@redocly/openapi-core": patch +--- + +Updated sourceDescriptions to require a valid type, to align with the Arazzo specification. diff --git a/__tests__/check-config/wrong-config-type-extensions-in-assertions/snapshot.js b/__tests__/check-config/wrong-config-type-extensions-in-assertions/snapshot.js index fe75e0df16..eb312666fa 100644 --- a/__tests__/check-config/wrong-config-type-extensions-in-assertions/snapshot.js +++ b/__tests__/check-config/wrong-config-type-extensions-in-assertions/snapshot.js @@ -5,7 +5,7 @@ exports[`E2E check-config wrong config type extension in assertions 1`] = ` Deprecated plugin format detected: type-extension [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", "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", "NoneSourceDescription", "ArazzoSourceDescription", "Parameters", "ReusableObject", "Workflows", "Workflow", "Steps", "Step", "Replacement", "ExtendedOperation", "ExpectSchema", "Outputs", "CriterionObject", "XPathCriterion", "JSONPathCriterion", "SuccessActionObject", "OnSuccessActionList", "FailureActionObject", "OnFailureActionList", "NamedInputs", "NamedSuccessActions", "NamedFailureActions", "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", "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", "ExpectSchema", "Outputs", "CriterionObject", "XPathCriterion", "JSONPathCriterion", "SuccessActionObject", "OnSuccessActionList", "FailureActionObject", "OnFailureActionList", "NamedInputs", "NamedSuccessActions", "NamedFailureActions", "SpecExtension". 8 | rule/metadata-lifecycle: 9 | subject: diff --git a/__tests__/lint-config/invalid-config-assertation-config-type/snapshot.js b/__tests__/lint-config/invalid-config-assertation-config-type/snapshot.js index d07c1737f5..b45b5c0013 100644 --- a/__tests__/lint-config/invalid-config-assertation-config-type/snapshot.js +++ b/__tests__/lint-config/invalid-config-assertation-config-type/snapshot.js @@ -6,7 +6,7 @@ exports[`E2E lint-config test with option: { dirName: 'invalid-config-assertatio The 'assert/' syntax in assert/path-item-mutually-required is deprecated. Update your configuration to use 'rule/' instead. Examples and more information: https://redocly.com/docs/cli/rules/configurable-rules/ [1] .redocly.yaml:9:17 at #/rules/assert~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", "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", "NoneSourceDescription", "ArazzoSourceDescription", "Parameters", "ReusableObject", "Workflows", "Workflow", "Steps", "Step", "Replacement", "ExtendedOperation", "ExpectSchema", "Outputs", "CriterionObject", "XPathCriterion", "JSONPathCriterion", "SuccessActionObject", "OnSuccessActionList", "FailureActionObject", "OnFailureActionList", "NamedInputs", "NamedSuccessActions", "NamedFailureActions", "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", "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", "ExpectSchema", "Outputs", "CriterionObject", "XPathCriterion", "JSONPathCriterion", "SuccessActionObject", "OnSuccessActionList", "FailureActionObject", "OnFailureActionList", "NamedInputs", "NamedSuccessActions", "NamedFailureActions", "SpecExtension". 7 | where: 8 | - subject: 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 d28851a81c..dbb7f20e89 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 @@ -13,6 +13,7 @@ exports[`resolveConfig should ignore minimal from the root and read local file 1 "requestBody-replacements-unique": "warn", "sourceDescription-name-unique": "error", "sourceDescription-type": "error", + "sourceDescriptions-not-empty": "error", "spec": "error", "step-onFailure-unique": "warn", "step-onSuccess-unique": "warn", @@ -158,6 +159,7 @@ exports[`resolveStyleguideConfig should resolve extends with local file config w "requestBody-replacements-unique": "warn", "sourceDescription-name-unique": "error", "sourceDescription-type": "error", + "sourceDescriptions-not-empty": "error", "spec": "error", "step-onFailure-unique": "warn", "step-onSuccess-unique": "warn", diff --git a/packages/core/src/config/all.ts b/packages/core/src/config/all.ts index a6dd21f804..56fb9645f2 100644 --- a/packages/core/src/config/all.ts +++ b/packages/core/src/config/all.ts @@ -142,6 +142,7 @@ const all: PluginStyleguideConfig<'built-in'> = { 'no-criteria-xpath': 'error', 'no-actions-type-end': 'error', 'criteria-unique': 'error', + 'sourceDescriptions-not-empty': 'error', }, }; diff --git a/packages/core/src/config/minimal.ts b/packages/core/src/config/minimal.ts index f1ae082ea6..9eca8896a9 100644 --- a/packages/core/src/config/minimal.ts +++ b/packages/core/src/config/minimal.ts @@ -124,6 +124,7 @@ const minimal: PluginStyleguideConfig<'built-in'> = { 'no-criteria-xpath': 'off', 'no-actions-type-end': 'off', 'criteria-unique': 'off', + 'sourceDescriptions-not-empty': 'off', }, }; diff --git a/packages/core/src/config/recommended-strict.ts b/packages/core/src/config/recommended-strict.ts index 9cd52552fe..17d44445ad 100644 --- a/packages/core/src/config/recommended-strict.ts +++ b/packages/core/src/config/recommended-strict.ts @@ -124,6 +124,7 @@ const recommendedStrict: PluginStyleguideConfig<'built-in'> = { 'no-criteria-xpath': 'error', 'no-actions-type-end': 'error', 'criteria-unique': 'error', + 'sourceDescriptions-not-empty': 'error', }, }; diff --git a/packages/core/src/config/recommended.ts b/packages/core/src/config/recommended.ts index 3a155d80c6..c918c2a2ba 100644 --- a/packages/core/src/config/recommended.ts +++ b/packages/core/src/config/recommended.ts @@ -124,6 +124,7 @@ const recommended: PluginStyleguideConfig<'built-in'> = { 'no-criteria-xpath': 'warn', 'no-actions-type-end': 'warn', 'criteria-unique': 'warn', + 'sourceDescriptions-not-empty': 'error', }, }; diff --git a/packages/core/src/rules/arazzo/__tests__/sourceDescriptions-not-empty.test.ts b/packages/core/src/rules/arazzo/__tests__/sourceDescriptions-not-empty.test.ts new file mode 100644 index 0000000000..035c29e298 --- /dev/null +++ b/packages/core/src/rules/arazzo/__tests__/sourceDescriptions-not-empty.test.ts @@ -0,0 +1,106 @@ +import { outdent } from 'outdent'; +import { lintDocument } from '../../../lint'; +import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../../../__tests__/utils'; +import { BaseResolver } from '../../../resolve'; + +describe('Arazzo sourceDescriptions-not-empty', () => { + const document1 = parseYamlToDocument( + outdent` + arazzo: '1.0.0' + info: + title: Cool API + version: 1.0.0 + description: A cool API + sourceDescriptions: + - name: museum-api + type: openapi + url: openapi.yaml + - name: museum-api + type: openapi + url: openapi.yaml + workflows: + - workflowId: get-museum-hours + description: This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from openapi.yaml description. + operationId: museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + `, + 'arazzo.yaml' + ); + + const document2 = parseYamlToDocument( + outdent` + arazzo: '1.0.0' + info: + title: Cool API + version: 1.0.0 + description: A cool API + sourceDescriptions: [] + workflows: + - workflowId: get-museum-hours + description: This workflow demonstrates how to get the museum opening hours and buy tickets. + parameters: + - in: header + name: Authorization + value: Basic Og== + steps: + - stepId: get-museum-hours + description: >- + Get museum hours by resolving request details with getMuseumHours operationId from openapi.yaml description. + operationId: museum-api.getMuseumHours + successCriteria: + - condition: $statusCode == 200 + `, + 'arazzo.yaml' + ); + + it('should not report an error when sourceDescriptions have at least one entry.', async () => { + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document: document1, + config: await makeConfig({ + rules: {}, + arazzoRules: { 'sourceDescriptions-not-empty': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`); + }); + + it('should report an error when sourceDescriptions is empty list.', async () => { + const results = await lintDocument({ + externalRefResolver: new BaseResolver(), + document: document2, + config: await makeConfig({ + rules: {}, + arazzoRules: { 'sourceDescriptions-not-empty': 'error' }, + }), + }); + + expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(` + [ + { + "location": [ + { + "pointer": "#/sourceDescriptions", + "reportOnKey": false, + "source": "arazzo.yaml", + }, + ], + "message": "The \`sourceDescriptions\` list must have at least one entry.", + "ruleId": "sourceDescriptions-not-empty", + "severity": "error", + "suggest": [], + }, + ] + `); + }); +}); diff --git a/packages/core/src/rules/arazzo/index.ts b/packages/core/src/rules/arazzo/index.ts index c00261a2f3..66af82904c 100644 --- a/packages/core/src/rules/arazzo/index.ts +++ b/packages/core/src/rules/arazzo/index.ts @@ -1,7 +1,7 @@ import { Spec } from '../common/spec'; import { Assertions } from '../common/assertions'; import { ParametersNotInBody } from '../spot/parameters-not-in-body'; -import { SourceDescriptionType } from '../arazzo/source-description-type'; +import { SourceDescriptionType } from '../arazzo/sourceDescription-type'; import { VersionEnum } from '../spot/version-enum'; import { WorkflowIdUnique } from './workflowId-unique'; import { StepIdUnique } from './stepId-unique'; @@ -14,6 +14,7 @@ import { RequestBodyReplacementsUnique } from './requestBody-replacements-unique import { NoCriteriaXpath } from '../spot/no-criteria-xpath'; import { NoActionsTypeEnd } from '../spot/no-actions-type-end'; import { CriteriaUnique } from './criteria-unique'; +import { SourceDescriptionsNotEmpty } from './sourceDescriptions-not-empty'; import type { ArazzoRule } from '../../visitors'; import type { ArazzoRuleSet } from '../../oas-types'; @@ -35,6 +36,7 @@ export const rules: ArazzoRuleSet<'built-in'> = { 'no-criteria-xpath': NoCriteriaXpath as ArazzoRule, 'no-actions-type-end': NoActionsTypeEnd as ArazzoRule, 'criteria-unique': CriteriaUnique as ArazzoRule, + 'sourceDescriptions-not-empty': SourceDescriptionsNotEmpty as ArazzoRule, }; export const preprocessors = {}; diff --git a/packages/core/src/rules/arazzo/source-description-type.ts b/packages/core/src/rules/arazzo/sourceDescription-type.ts similarity index 68% rename from packages/core/src/rules/arazzo/source-description-type.ts rename to packages/core/src/rules/arazzo/sourceDescription-type.ts index efc4331394..c02f1b69b8 100644 --- a/packages/core/src/rules/arazzo/source-description-type.ts +++ b/packages/core/src/rules/arazzo/sourceDescription-type.ts @@ -4,13 +4,14 @@ import type { UserContext } from '../../walk'; export const SourceDescriptionType: ArazzoRule = () => { return { SourceDescriptions: { - enter(SourceDescriptions, { report, location }: UserContext) { - for (const sourceDescription of SourceDescriptions) { + enter(sourceDescriptions, { report, location }: UserContext) { + if (!sourceDescriptions.length) return; + for (const sourceDescription of sourceDescriptions) { if (!['openapi', 'arazzo'].includes(sourceDescription?.type)) { report({ message: 'The `type` property of the `sourceDescription` object must be either `openapi` or `arazzo`.', - location: location.child([SourceDescriptions.indexOf(sourceDescription)]), + location: location.child([sourceDescriptions.indexOf(sourceDescription)]), }); } } diff --git a/packages/core/src/rules/arazzo/sourceDescriptions-not-empty.ts b/packages/core/src/rules/arazzo/sourceDescriptions-not-empty.ts new file mode 100644 index 0000000000..17f17f15e0 --- /dev/null +++ b/packages/core/src/rules/arazzo/sourceDescriptions-not-empty.ts @@ -0,0 +1,17 @@ +import type { ArazzoRule } from '../../visitors'; +import type { UserContext } from '../../walk'; + +export const SourceDescriptionsNotEmpty: ArazzoRule = () => { + return { + SourceDescriptions: { + enter(sourceDescriptions, { report, location }: UserContext) { + if (!sourceDescriptions?.length) { + report({ + message: 'The `sourceDescriptions` list must have at least one entry.', + location, + }); + } + }, + }, + }; +}; diff --git a/packages/core/src/types/arazzo.ts b/packages/core/src/types/arazzo.ts index df67e7085a..2e58b7a067 100755 --- a/packages/core/src/types/arazzo.ts +++ b/packages/core/src/types/arazzo.ts @@ -50,10 +50,8 @@ const SourceDescriptions: NodeType = { items: (value: any) => { if (value?.type === 'openapi') { return 'OpenAPISourceDescription'; - } else if (value?.type === 'arazzo') { - return 'ArazzoSourceDescription'; } else { - return 'NoneSourceDescription'; + return 'ArazzoSourceDescription'; } }, }; @@ -67,15 +65,6 @@ const OpenAPISourceDescription: NodeType = { required: ['name', 'type', 'url'], extensionsPrefix: 'x-', }; -const NoneSourceDescription: NodeType = { - properties: { - name: { type: 'string' }, - type: { type: 'string', enum: ['none'] }, - 'x-serverUrl': { type: 'string' }, - }, - required: ['name', 'type', 'x-serverUrl'], - extensionsPrefix: 'x-', -}; const ArazzoSourceDescription: NodeType = { properties: { name: { type: 'string' }, @@ -288,7 +277,6 @@ export const ArazzoTypes: Record = { Info, SourceDescriptions, OpenAPISourceDescription, - NoneSourceDescription, ArazzoSourceDescription, Parameters, Parameter, diff --git a/packages/core/src/types/redocly-yaml.ts b/packages/core/src/types/redocly-yaml.ts index dc8675ef63..5413083793 100644 --- a/packages/core/src/types/redocly-yaml.ts +++ b/packages/core/src/types/redocly-yaml.ts @@ -125,6 +125,7 @@ const builtInArazzoRules = [ 'no-criteria-xpath', 'no-actions-type-end', 'criteria-unique', + 'sourceDescriptions-not-empty', ] as const; export type BuiltInArazzoRuleId = typeof builtInArazzoRules[number]; diff --git a/packages/core/src/typings/arazzo.ts b/packages/core/src/typings/arazzo.ts index 77037f5e5c..d0c71365a1 100644 --- a/packages/core/src/typings/arazzo.ts +++ b/packages/core/src/typings/arazzo.ts @@ -12,22 +12,13 @@ export interface OpenAPISourceDescription { 'x-serverUrl'?: string; } -export interface NoneSourceDescription { - name: string; - type: 'none'; - 'x-serverUrl': string; -} - export interface ArazzoSourceDescription { name: string; type: 'arazzo'; url: string; } -export type SourceDescription = - | OpenAPISourceDescription - | NoneSourceDescription - | ArazzoSourceDescription; +export type SourceDescription = OpenAPISourceDescription | ArazzoSourceDescription; export interface Parameter { in?: 'header' | 'query' | 'path' | 'cookie' | 'body'; diff --git a/packages/core/src/visitors.ts b/packages/core/src/visitors.ts index ac7aa00cab..4cf6d0b41d 100644 --- a/packages/core/src/visitors.ts +++ b/packages/core/src/visitors.ts @@ -58,7 +58,6 @@ import type { ExpectSchema, ExtendedOperation, InfoObject, - NoneSourceDescription, OnFailureObject, OnSuccessObject, OpenAPISourceDescription, @@ -240,7 +239,6 @@ type ArazzoFlatVisitor = { ParameterObject?: VisitFunctionOrObject; InfoObject?: VisitFunctionOrObject; OpenAPISourceDescription?: VisitFunctionOrObject; - NoneSourceDescription?: VisitFunctionOrObject; ArazzoSourceDescription?: VisitFunctionOrObject; SourceDescription?: VisitFunctionOrObject; ExtendedOperation?: VisitFunctionOrObject;