Custom Functions for Spectral (running locally)

I’d like to switch us over to spectral for OpenAPI spec validation of our many API specs and I think the custom validation rules are a very compelling feature - but I am struggling to get a simple example working. In this case, I would like to ensure that our parameter names contain no more than one underscore.

From https://stoplight.io/p/docs/gh/stoplightio/spectral/docs/guides/custom-functions.md I have created a spectral.yml which does get picked up because I’m also disabling some rules that we don’t comply with (not all the operations have descriptions and some of our examples seem to be the wrong datatype!). I’m completely unclear:

  • how to tell if the rule is even getting called
  • how to tell it to run on parameter elements
  • what the function should return, for both success and for failure
  • whether I should be able to see console.log output in my custom function when I’m trying to debug things

Any help is appreciated and I’ll try to blog what I come up with as well once it’s all sorted - this would be a superpower if I could make it work :slight_smile:

Hey! Not involved with the development of this project but I just did some custom functions over the past few days and figured I would share what I could to help out.

  1. I made use of console.log to determine if things were actually getting called or not. It does print out (for me, above the standard rule error output that Spectral does)
  2. I haven’t been able to get the functionOptions parameters working, so if you figure that out I’d love to know how you did it.
  3. As far as I can tell, the function returns an empty array if successful and a singleton array with an error object with a message about what went wrong if it failed (where you specify the failure conditions in the function).

Here’s the example I got working which is simply checking whether a string “pagination” exists within an array (which is what targetVal is for us). You’ll see if the string is NOT detected, then we return

module.exports = (targetVal) => {
const valueExists = 'pagination';

if (!targetVal.includes(valueExists)) {
    return [
        {
          message: `The value {valueExists} was not found.`,
        },
      ];
}

return [];
};

I hope this helps! We’re working on putting together a cookbook of common situations we are running into outside the standard cases documented and it would hopefully make its way somewhere useful for other people to use.

1 Like

For what it’s worth, figured out the passing of parameters. It’s using the same opts parameter the built in functions are using, in the same way.

module.exports = (targetVal, opts) => {
    const { valueExists } = opts;

    if (!targetVal.includes(valueExists)) {
        return [
            {
              message: `The value {valueExists} was not found in the array {targetVal}.`,
            },
          ];
    }

    return [];
  };

So now in your yaml you can import the exists function and then do something like:

    then:
      field: tags
      function: exists
      functionOptions:
        valueExists: somethingYouWantToCheck
1 Like