question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Custom functions in Rulesets

See original GitHub issue

User Story

As a ruleset maintainer, I would like to be able to define my own functions, to achieve things the built in functions might not allow. How that is done I do not much care, so long as it is easy, and the rulesets remain portable as simple YAML or JSON files.

Details

Both the CLI and the JS API are converging on a simple loadRulesets() method (#366), leaving behind the separate loadRules() and loadFunctions() methods that have been there forever.

This will make things easier for Studio, and all JS API users, but we still need a way to support custom functions. Those functions should be defined in, or at the very least referenced by, the ruleset.

A complementary feature that needs to be delivered as a part of this story is the function options schema. In other words - we need to have to configure the function without falling back to JS code too much.

Implementation

{
  // recommended rules are on, using their default severity values
  // it is expected that an `index.json` file results at the target location if the path does not end in `.json`
  "extends": "spectral:oas2",

  // OPTIONAL
  // use to customize where functions are located, by default they are located in the `functions` dir relative to this ruleset file
  "functionsDir": "functions",

  // OPTIONAL - only needed if defining new functions for your ruleset
  "functions": {
    // convention is that there must be a corresponding `functions/op-id-unique.js` file relative to this config file's location
    "op-id-unique": {
      // OPTIONAL the json schema that describes the function options, if any
      "schema": {
        "type": "object",
        "properties": {
          "foo": {
            "type": "string"
          }
        }
      }
    }
  },

  "rules": {
    // define a new rule that uses a the custom function defined in this config (op-id-unique)
    "my-rule": {
      "summary": "Every operation must have a unique `operationId`.",
      "type": "validation",
      "severity": "error",
      "given": "$",
      "then": {
        "function": "op-id-unique",
        "functionOptions": {
          "foo": "hello"
        }
      }
    }
  }
}

When we switch to doing this, lets implement this approach for all of our core functions, and clean out all of the old TypeScript definitions for them, so there is no different between core functions and custom functions.

export interface ILengthRuleOptions {
  min?: number;
  max?: number;
}
export type LengthRule = IRule<RuleFunction.LENGTH, ILengthRuleOptions>;

We can also clean out the function collections in our rulesets index.ts:

export const commonOasFunctions = (): FunctionCollection => {
  return {
    oasPathParam: require('./functions/oasPathParam').oasPathParam,
    oasOp2xxResponse: require('./functions/oasOp2xxResponse').oasOp2xxResponse,
    oasOpSecurityDefined: require('./functions/oasOpSecurityDefined').oasOpSecurityDefined, // used in oas2/oas3 differently see their rulesets for details
    oasOpIdUnique: require('./functions/oasOpIdUnique').oasOpIdUnique,
    oasOpFormDataConsumeCheck: require('./functions/oasOpFormDataConsumeCheck').oasOpFormDataConsumeCheck,
    oasOpParams: require('./functions/oasOpParams').oasOpParams,
  };
};

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:10 (10 by maintainers)

github_iconTop GitHub Comments

1reaction
philsturgeoncommented, Aug 12, 2019

I think binding too, because two rulesets with different implementations could break each other and that sounds a lot worse than the alternative: a ruleset author having to copy and paste a thing or find a way to get that same function picked up by their ruleset.

Let’s bind to the ruleset and see what people say.

0reactions
P0lipcommented, Aug 11, 2019

Alright, one more design decision to make.

@philsturgeon @marbemac How should functions extending looks like? I can think of 2 possible solutions:

  • we override the definitions same as we do with rules
  • we bind each function definition to actual ruleset

The latter is more difficult to implement, but I believe that this is the way to go, since a given rule might depend on particular implementation of the function. We’ll never be sure whether the other overriding function would have similar behavior and would produce the same output.

What do you all think? Do you get my point or shall I elaborate more?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Write a rule that uses a custom function
A function written in Java or .NET, conforming to a particular interface that makes it accessible from within a rulebase. Inputs are defined...
Read more >
Using Custom Functions in a Rule Set or Code File
In the rule set or code file, you can reference custom functions in the same way that you reference functions that are provided...
Read more >
Custom Functions | Spectral - Stoplight
If the core functions aren't enough for your custom ruleset, Spectral allows you to write and use custom functions. Powered by Stoplight.
Read more >
Business Rules 301: Custom Functions - ChannelAdvisor
Custom functions will generate the same output and can be called from multiple different rules to generate that same output.
Read more >
Custom RuleSets - vacuum OpenAPI Linter - quobix
At some point however, there comes a time to customize the rules and perhaps add some new ones using built-in core functions.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found