Repository: stoplightio/spectral
Pull request #2581 (2024-01-22 18:23:56)

feat(rulesets): add oas3_1-servers-in-webhook and oas3_1-callbacks-in…
…-webhook rules

Fixes #[ISSUE_NUMBER].

**Checklist**

- [x] Tests added / updated
- [x] Docs added / updated

**Does this PR introduce a breaking change?**

- [ ] Yes
- [x] No

> If indicated yes above, please describe the breaking change(s).
>
> **Remove this quote before creating the PR.**

**Screenshots**

If applicable, add screenshots or gifs to help demonstrate the changes. If not applicable, remove this screenshots section before creating the PR.

**Additional context**

Added two new spectral rules:
- oas3_1-servers-in-webhook
  - warns if servers is defined in a webhook
- oas3_1-callbacks-in-webhook
  - warns if callbacks is defined in webhook operation



Patch:
diff --git a/docs/reference/openapi-rules.md b/docs/reference/openapi-rules.md
index 0ea70cde..56b06601 100644
--- a/docs/reference/openapi-rules.md
+++ b/docs/reference/openapi-rules.md
@@ -920,3 +920,66 @@ servers:
 ```
 
 In this example, both **`{region}`** and **`{version}`** variables are properly defined and used in the server URL. Also, the default value for **`region`** is within the allowed values.
+
+### oas3_callbacks_in_callbacks
+
+A callback should not be defined within another callback.
+
+**Recommended:** Yes
+
+**Bad Example**
+
+```yaml
+paths:
+  /path:
+    get:
+      callbacks:
+        onData:
+          /data:
+            post:
+              callbacks: ...
+```
+
+### oas3_1-servers-in-webhook
+
+Servers should not be defined in a webhook.
+
+**Recommended:** Yes
+
+**Bad Example**
+
+At the path item object level:
+
+```yaml
+webhooks:
+  servers:
+    - url: https://example.com/
+    - url: https://example.com/api/
+```
+
+or
+
+At the operation level:
+
+```yaml
+webhooks:
+  newPet:
+    post:
+      servers:
+        -url: https://example.com/
+```
+
+### oas3_1-callbacks-in-webhook
+
+Callbacks should not be defined in a webhook.
+
+**Recommended:** Yes
+
+**Bad Example**
+
+```yaml
+webhooks:
+  newPet:
+    post:
+      callbacks: ...
+```
diff --git a/packages/rulesets/src/oas/index.ts b/packages/rulesets/src/oas/index.ts
index f6f0e051..67af689d 100644
--- a/packages/rulesets/src/oas/index.ts
+++ b/packages/rulesets/src/oas/index.ts
@@ -707,5 +707,32 @@ const ruleset = {
         },
       },
     },
+    'oas3-callbacks-in-callbacks': {
+      message: 'Callbacks should not be defined within a callback',
+      formats: [oas3],
+      recommended: true,
+      given: ['#OperationObject.callbacks[*][*][*].callbacks'],
+      then: {
+        function: undefined,
+      },
+    },
+    'oas3_1-servers-in-webhook': {
+      message: 'Servers should not be defined in a webhook.',
+      formats: [oas3_1],
+      recommended: true,
+      given: ['$.webhooks.servers', '$.webhooks[*][*].servers'],
+      then: {
+        function: undefined,
+      },
+    },
+    'oas3_1-callbacks-in-webhook': {
+      message: 'Callbacks should not be defined in a webhook.',
+      formats: [oas3_1],
+      recommended: true,
+      given: ['$.webhooks[*][*].callbacks'],
+      then: {
+        function: undefined,
+      },
+    },
   },
 };


Test patch:
diff --git a/packages/rulesets/src/oas/__tests__/oas3-callbacks-in-callbacks.test.ts b/packages/rulesets/src/oas/__tests__/oas3-callbacks-in-callbacks.test.ts
new file mode 100644
index 00000000..6c3a5413
--- /dev/null
+++ b/packages/rulesets/src/oas/__tests__/oas3-callbacks-in-callbacks.test.ts
@@ -0,0 +1,33 @@
+import { DiagnosticSeverity } from '@stoplight/types';
+import testRule from '../../__tests__/__helpers__/tester';
+
+testRule('oas3-callbacks-in-callbacks', [
+  {
+    name: 'callbacks defined within a callback',
+    document: {
+      openapi: '3.0.0',
+      paths: {
+        '/path': {
+          get: {
+            callbacks: {
+              onData: {
+                '/data': {
+                  post: {
+                    callbacks: {},
+                  },
+                },
+              },
+            },
+          },
+        },
+      },
+    },
+    errors: [
+      {
+        message: 'Callbacks should not be defined within a callback',
+        path: ['paths', '/path', 'get', 'callbacks', 'onData', '/data', 'post', 'callbacks'],
+        severity: DiagnosticSeverity.Warning,
+      },
+    ],
+  },
+]);
diff --git a/packages/rulesets/src/oas/__tests__/oas3_1-callbacks-in-webhook.test.ts b/packages/rulesets/src/oas/__tests__/oas3_1-callbacks-in-webhook.test.ts
new file mode 100644
index 00000000..0706cdf6
--- /dev/null
+++ b/packages/rulesets/src/oas/__tests__/oas3_1-callbacks-in-webhook.test.ts
@@ -0,0 +1,25 @@
+import { DiagnosticSeverity } from '@stoplight/types';
+import testRule from '../../__tests__/__helpers__/tester';
+
+testRule('oas3_1-callbacks-in-webhook', [
+  {
+    name: 'callbacks defined in webhook',
+    document: {
+      openapi: '3.1.0',
+      webhooks: {
+        newPet: {
+          post: {
+            callbacks: {},
+          },
+        },
+      },
+    },
+    errors: [
+      {
+        message: 'Callbacks should not be defined in a webhook.',
+        path: ['webhooks', 'newPet', 'post', 'callbacks'],
+        severity: DiagnosticSeverity.Warning,
+      },
+    ],
+  },
+]);
diff --git a/packages/rulesets/src/oas/__tests__/oas3_1-servers-in-webhook.test.ts b/packages/rulesets/src/oas/__tests__/oas3_1-servers-in-webhook.test.ts
new file mode 100644
index 00000000..3e2ebe43
--- /dev/null
+++ b/packages/rulesets/src/oas/__tests__/oas3_1-servers-in-webhook.test.ts
@@ -0,0 +1,31 @@
+import { DiagnosticSeverity } from '@stoplight/types';
+import testRule from '../../__tests__/__helpers__/tester';
+
+testRule('oas3_1-servers-in-webhook', [
+  {
+    name: 'servers defined in webhook',
+    document: {
+      openapi: '3.1.0',
+      webhooks: {
+        servers: [],
+        newPet: {
+          post: {
+            servers: [],
+          },
+        },
+      },
+    },
+    errors: [
+      {
+        message: 'Servers should not be defined in a webhook.',
+        path: ['webhooks', 'servers'],
+        severity: DiagnosticSeverity.Warning,
+      },
+      {
+        message: 'Servers should not be defined in a webhook.',
+        path: ['webhooks', 'newPet', 'post', 'servers'],
+        severity: DiagnosticSeverity.Warning,
+      },
+    ],
+  },
+]);