cjoudrey / graphql-schema-linter

Validate GraphQL schema definitions against a set of rules
MIT License
694 stars 62 forks source link

proposal: lint schema from url #244

Closed mac2000 closed 3 years ago

mac2000 commented 4 years ago

In case the project uses code first approach (e.g. there is no schema file) there is no way to lint it, will be so much easier if we could do something like npx graphql-schema-linter http://localhost:4000

Workaround: at moment we can do something like:

schema=$(mktemp).graphql
npx apollo client:download-schema --endpoint=http://localhost:4000 $schema
npx graphql-schema-linter $schema
cjoudrey commented 4 years ago

Hey @mac2000, thanks for opening this issue.

Sorry for the late reply.

At this time this is not possible, however, have you considered using --stdin instead of using a tempfile?

I'm not familiar with apollo client:download-schema but if it has a way to output to stdout, you could pipe it to graphql-schema-linter --stdin.

mac2000 commented 4 years ago

Hm, nope, apollo tools does output info messages so passing /dev/stdout as a target does not solve the issue

image

But indeed it will work with any other tool that is able to retrieve schema from endpoint, my personal wish to have less tools around this one, so can use simple command in CI process

mac2000 commented 3 years ago

After a while, it seems that this one is not required anymore. In the end, we start to configure linter more and more and even create a bunch of our custom rules. As a result, we just created a dedicated repository that holds all rules and configurations and serves as a kind of wrapper around the original linter which allows us not only to download schema from URL but much more

Here is a pseudo-code to give an idea for anyone interested:

cli.js

#!/usr/bin/env node

const retrieve = require('./utils/retrieve')
const lint = require('./utils/lint')

retrieve(process.argv[2])
  .then(lint)
  .then(print)
  .then(process.exit)

Where:

lint.js

this one is blind copypasta from linter with few hardcoded configurations nothing fancy

const path = require('path')
const retrieve = require('./retrieve')
const { loadSchema } = require('graphql-schema-linter/lib/schema')
const { Configuration } = require('graphql-schema-linter/lib/configuration')
const { validateSchemaDefinition } = require('graphql-schema-linter/lib/validator')
const options = require('../graphql-schema-linter.config')

options.customRulePaths = options.customRulePaths.map(p => path.join(__dirname, '../', p))

const lint = async pathOrUrl => {
  if (!pathOrUrl) {
    return {
      pathOrUrl: pathOrUrl,
      code: 2,
      message: 'Schema missing. Pass it as first argument. it should be path to a graphql file or url to graphql endpoint',
      issues: [],
      errors: [],
    }
  }

  const schemaPath = await retrieve(pathOrUrl)
  const schema = await loadSchema({ schemaPaths: [schemaPath] })
  if (schema === null) {
    return {
      pathOrUrl: pathOrUrl,
      code: 2,
      message: 'No valid schema input.',
      issues: [],
      errors: [],
    }
  }

  const configuration = new Configuration(schema, options)
  const issues = configuration.validate()
  if (issues.some(({ type }) => type === 'error')) {
    return {
      pathOrUrl: pathOrUrl,
      code: 2,
      message: 'There were issues with linter configuration.',
      issues: issues,
      errors: [],
    }
  }

  const rules = configuration.getRules()
  const errors = validateSchemaDefinition(schema, rules, configuration)

  return {
    pathOrUrl: pathOrUrl,
    code: errors.length ? 1 : 0,
    message: errors.length ? `${errors.length} errors detected` : 'Schema is valid.',
    issues: issues.map(({ type, field, message }) => ({ type, field, message })),
    errors: errors.map(({ ruleName, message }) => ({ rule: ruleName, message })),
  }
}

module.exports = lint

So now we have everything in one place as a npm package which is used to lint all our service

@cjoudrey thank you for awesome library!