Repository: stoplightio/spectral
Pull request #2460 (2023-04-21 11:08:39)

feat(rulesets): add message traits array path to asyncapi headers schema type object rule
Recreation of #2402 to fix issues around commitlint

**Checklist**

- [x] Tests added / updated
- [ ] Docs added / updated

**Does this PR introduce a breaking change?**

- [ ] Yes
- [x] No

**Additional context**

Currently, the `asyncapi-headers-schema-type-object` will not trigger when the `headers` object is defined explicitly in the `message.traits` array. 
For example:
```yaml
...
channels:
  'users/{userId}/loggedIn':
    publish:
      message:
        traits:
          - headers
              type: integer
```

The new path added in this PR will match on that array and perform the assertion on the `type` field of any object in that array that has a top level key of `headers`.

CalemRoelofsSB: ping @P0lip 
P0lip: Looks good to me, although I'd wait for an approval from our AsyncAPI colleagues as they're the most familiar with it :)
CalemRoelofsSB: @P0lip @jonaslagoni thank you for the review folks! Unfortunately, I don't have permissions to merge this branch into `develop` so if one of you could oblige, that would be most appreciated :) 

Patch:
diff --git a/packages/rulesets/src/asyncapi/index.ts b/packages/rulesets/src/asyncapi/index.ts
index 94221b93..eb20fa1f 100644
--- a/packages/rulesets/src/asyncapi/index.ts
+++ b/packages/rulesets/src/asyncapi/index.ts
@@ -90,6 +90,7 @@ export default {
         '$.components.messageTraits.*.headers',
         '$.components.messages.*.headers',
         '$.channels.*.[publish,subscribe].message.headers',
+        '$.channels.*.[publish,subscribe].message.traits[*].headers',
       ],
       then: {
         function: schema,


Test patch:
diff --git a/packages/rulesets/src/asyncapi/__tests__/asyncapi-headers-schema-type-object.test.ts b/packages/rulesets/src/asyncapi/__tests__/asyncapi-headers-schema-type-object.test.ts
index 36e99de4..c0b971b0 100644
--- a/packages/rulesets/src/asyncapi/__tests__/asyncapi-headers-schema-type-object.test.ts
+++ b/packages/rulesets/src/asyncapi/__tests__/asyncapi-headers-schema-type-object.test.ts
@@ -26,6 +26,18 @@ const document = {
         message: cloneDeep(headersBearer),
       },
     },
+    'users/{userId}/loggedIn': {
+      publish: {
+        message: {
+          traits: [cloneDeep(headersBearer)],
+        },
+      },
+      subscribe: {
+        message: {
+          traits: [cloneDeep(headersBearer)],
+        },
+      },
+    },
   },
   components: {
     messageTraits: {
@@ -131,5 +143,34 @@ testRule('asyncapi-headers-schema-type-object', [
         },
       ],
     },
+
+    {
+      name: `channels.{channel}.${property}.message.traits.[*].headers lacks "type" property`,
+      document: produce(document, draft => {
+        draft.channels['users/{userId}/loggedIn'][property].message.traits[0].headers = { const: 'Hello World!' };
+      }),
+      errors: [
+        {
+          message: 'Headers schema type must be "object" ("headers" property must have required property "type").',
+          path: ['channels', 'users/{userId}/loggedIn', property, 'message', 'traits', '0', 'headers'],
+          severity: DiagnosticSeverity.Error,
+        },
+      ],
+    },
+
+    {
+      name: `channels.{channel}.${property}.message.traits.[*].headers is not of type "object"`,
+      document: produce(document, draft => {
+        draft.channels['users/{userId}/loggedIn'][property].message.traits[0].headers = { type: 'integer' };
+      }),
+      errors: [
+        {
+          message:
+            'Headers schema type must be "object" ("type" property must be equal to one of the allowed values: "object". Did you mean "object"?).',
+          path: ['channels', 'users/{userId}/loggedIn', property, 'message', 'traits', '0', 'headers', 'type'],
+          severity: DiagnosticSeverity.Error,
+        },
+      ],
+    },
   ]),
 ]);