spectral icon indicating copy to clipboard operation
spectral copied to clipboard

Docs: How to use the JavaScript API with `extends`

Open jamietanna opened this issue 3 years ago • 5 comments

User Story

As a user, I want to write tests for my Spectral rules, so I can be more confident that the rules work.

Further details

I want to write tests, similar to the below:

const { Spectral, isOpenApiv3, Document } = require('@stoplight/spectral-core');
const Parsers = require("@stoplight/spectral-parsers"); // make sure to install the package if you intend to use default parsers!
const { truthy } = require("@stoplight/spectral-functions"); // this has to be installed as well
const yaml = require('js-yaml')
const fs  = require('fs')
const path  = require('path')

const myDocument = new Document(
  `---
responses:
  '200':
    description: ''`,
  Parsers.Yaml,
  "/my-file",
);

const spectral = new Spectral({});

var ruleset = {
  extends: [
    'spectral:oas'
  ]
}

spectral.setRuleset(ruleset);
spectral.run(myDocument).then(console.log);

However, this fails due to:

        throw new Error('Provided ruleset is not an object');
        ^

Error: Provided ruleset is not an object
    at assertValidRuleset (/home/jamie/workspaces/cddo/api-standards-linting/govuk-public-api-rules/node_modules/@stoplight/spectral-core/dist/ruleset/validation.js:94:15)
    at new Ruleset (/home/jamie/workspaces/cddo/api-standards-linting/govuk-public-api-rules/node_modules/@stoplight/spectral-core/dist/ruleset/ruleset.js:32:49)
    at /home/jamie/workspaces/cddo/api-standards-linting/govuk-public-api-rules/node_modules/@stoplight/spectral-core/dist/ruleset/ruleset.js:74:37
    at Array.reduce (<anonymous>)
    at new Ruleset (/home/jamie/workspaces/cddo/api-standards-linting/govuk-public-api-rules/node_modules/@stoplight/spectral-core/dist/ruleset/ruleset.js:61:99)
    at Spectral.setRuleset (/home/jamie/workspaces/cddo/api-standards-linting/govuk-public-api-rules/node_modules/@stoplight/spectral-core/dist/spectral.js:72:73)
    at Object.<anonymous> (/home/jamie/workspaces/cddo/api-standards-linting/govuk-public-api-rules/example.js:25:10)

I can't see it mentioned in the docs how to use extends with the JS API.

Unfortunately the solution noted in https://github.com/stoplightio/spectral/issues/1151 doesn't work any more as there's no assets.json available, nor does registerStaticAssets.

Versions

    "@stoplight/spectral-cli": "^6.1.0",
        "@stoplight/json": "3.17.0",
        "@stoplight/path": "1.3.2",
        "@stoplight/spectral-core": "^1.5.1",
        "@stoplight/spectral-parsers": "^1.0.1",
        "@stoplight/spectral-ref-resolver": "1.0.1",
        "@stoplight/spectral-ruleset-bundler": "^1.0.0",
        "@stoplight/spectral-ruleset-migrator": "^1.5.0",
        "@stoplight/spectral-rulesets": ">=1",
        "@stoplight/spectral-runtime": "^1.1.0",
        "@stoplight/types": "12.3.0",

jamietanna avatar Dec 22 '21 10:12 jamietanna

Hey! One should do the following

const { oas } = require('@stoplight/spectral-rulesets');

var ruleset = {
  extends: [oas]
};

Hope that helps. You are more than welcome to update the documentation!

P0lip avatar Dec 28 '21 19:12 P0lip

Thanks! I've also been able to get this working with steps as documented in https://www.jvt.me/posts/2021/12/22/spectral-jest/:

// Apache 2.0

const { fetch } = require('@stoplight/spectral-runtime')
const { Spectral, Document } = require('@stoplight/spectral-core')
const Parsers = require('@stoplight/spectral-parsers')
const fs = require('fs')
const path = require('path')
// we need to add `dist/loader/node` as per convo on https://github.com/stoplightio/spectral/issues/1956#issuecomment-999643841
const { bundleAndLoadRuleset } = require('@stoplight/spectral-ruleset-bundler/dist/loader/node')

const retrieveRuleset = async (filePath) => {
  return await bundleAndLoadRuleset(path.resolve(filePath), { fs, fetch })
}

const retrieveDocument = (filePath) => {
  const resolved = path.resolve(path.join('test/testdata', filePath))
  const body = fs.readFileSync(resolved, 'utf8')
  return new Document(body, Parsers.Yaml, filePath)
}

const setupSpectral = async () => {
  const ruleset = await retrieveRuleset('ruleset.yaml')
  const spectral = new Spectral()
  spectral.setRuleset(ruleset)
  return spectral
}

module.exports = {
  retrieveDocument,
  setupSpectral
}

Would it be best for me to raise a PR to get the docs page updated so there's a bit more info in there?

jamietanna avatar Jan 04 '22 15:01 jamietanna

Would it be best for me to raise a PR to get the docs page updated so there's a bit more info in there?

sure, that'd be awesome.

P0lip avatar Jan 04 '22 15:01 P0lip

FWIW I read your article, it's nice.

Note that you may run into Cannot find module 'nimma/legacy' errors, which can be solved through Jest configuration, which can be seen in the jest.config.js in the sample repo.

It might be good to mention here that this should work out of the box in Jest 28. https://github.com/facebook/jest/issues/9771 There might be some other nice workarounds there too which you may want to link.

P0lip avatar Jan 04 '22 15:01 P0lip

@P0lip extends: [oas] doesn't typecheck, though it does work at runtime...

Type '{ documentationUrl: string; formats: Format<void>[]; aliases: { PathItem: string[]; OperationObject: string[]; }; rules: { 'operation-success-response': { description: string; recommended: boolean; given: string; then: { ...; }; }; ... 49 more ...; 'oas3-unused-component': { ...; }; }; }' is not assignable to type 'RulesetDefinition | [RulesetDefinition, FileRulesetSeverityDefinition]'.
  Property 'extends' is missing in type '{ documentationUrl: string; formats: Format<void>[]; aliases: { PathItem: string[]; OperationObject: string[]; }; rules: { 'operation-success-response': { description: string; recommended: boolean; given: string; then: { ...; }; }; ... 49 more ...; 'oas3-unused-component': { ...; }; }; }' but required in type 'Readonly<{ documentationUrl?: string | undefined; description?: string | undefined; formats?: Formats<Format> | Format[] | undefined; parserOptions?: Partial<ParserOptions> | undefined; overrides?: RulesetOverridesDefinition | undefined; aliases?: RulesetAliasesDefinition | undefined; } & Readonly<...>>'.ts(2322)
types.d.ts(66, 5): 'extends' is declared here.
    "@stoplight/spectral-core": "^1.18.0",
    "@stoplight/spectral-rulesets": "^1.16.0",
    "@stoplight/types": "^13.13.0",

jedwards1211 avatar May 02 '23 03:05 jedwards1211