Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic middleware system #4

Merged
merged 14 commits into from
Jun 25, 2024
Merged

Basic middleware system #4

merged 14 commits into from
Jun 25, 2024

Conversation

benjie
Copy link
Member

@benjie benjie commented Jun 24, 2024

Description

Add your own validations via a plugin system.

src/index.ts:

import type {} from "gqlcheck";
import { MyPlugin } from "./plugin.js";

declare global {
  namespace GraphileConfig {
    interface GraphQLCheckConfig {
      /** Description here */
      mySetting?: boolean;
    }
  }
}

export const MyPreset: GraphileConfig.Preset = {
  plugins: [MyPlugin],
};

src/plugin.ts:

import type {} from "gqlcheck";
import { MyVisitor } from "./MyVisitor.js";

export const MyPlugin: GraphileConfig.Plugin = {
  name: "MyPlugin",
  version: "0.0.0",
  gqlcheck: {
    middleware: {
      visitors(next, event) {
        event.visitors.push(MyVisitor(event.rulesContext));
        return next();
      },
    },
  },
};

src/MyVisitor.ts:

import { RuleError, type RulesContext } from "gqlcheck";
import type { ASTVisitor } from "graphql";

export function MyVisitor(context: RulesContext): ASTVisitor {
  // Extract configuration.
  const {
    gqlcheck: {
      config: { mySetting = true } = {},
      operationOverrides = Object.create(null),
    } = {},
  } = context.getResolvedPreset();
  return {
    Field(node) {
      // Get GraphQL stuff from here rather than `import` to avoid conflicts
      const { isListType, getNullableType } = context.graphqlLibrary;
      const fieldDef = context.getFieldDef();

      const nodes = [node];
      // A node may be used in more than one operation.
      const allOperationNames = context.getOperationNamesForNodes(nodes);
      // Filter to just the operations where this setting is enabled.
      const operationNames = allOperationNames.filter(
        (n) =>
          // Can only use operationOverrides if the operation is named.
          (n ? operationOverrides[n]?.mySetting : null) ?? mySetting,
      );
      // Find the locations where the setting is enabled.
      const ops = context.getErrorOperationLocationsForNodes(
        nodes,
        operationNames,
      );

      // Only report if there are locations where the setting is enabled.
      if (ops.length > 0) {
        // Nonsense rule, demonstration purposes only. Forbids lists.
        const isList = isListType(getNullableType(fieldDef.type));
        if (isList) {
          context.reportError(
            new RuleError(`Querying lists is not allowed!`, {
              // Typically this will be the name of the setting.
              infraction: `mySetting`,
              nodes,
              errorOperationLocations: ops,
              // If the user merges this override in for this operation, the
              // error should go away.
              override: {
                mySetting: false,
              },
            }),
          );
        }
      }
    },
  };
}

Performance impact

Overhead, but worthwhile.

Security impact

You can now use arbitrary third party plugins... Only use plugins you trust.

Checklist

  • My code matches the project's code style and yarn lint:fix passes.
  • I've added tests for the new feature, and yarn test passes.
  • I have detailed the new feature in the relevant documentation.
  • I have added this feature to 'Pending' in the RELEASE_NOTES.md file (if one exists).
  • If this is a breaking change I've explained why.

@benjie benjie merged commit 7e2912c into main Jun 25, 2024
3 checks passed
@benjie benjie deleted the middlewares branch June 25, 2024 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant