diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5cee03c..55ac198 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,6 +32,7 @@ jobs: ${{ runner.OS }}-node- ${{ runner.OS }}- - run: npm ci + - run: npm run test-typescript - run: npm test - name: Coveralls Parallel uses: coverallsapp/github-action@master diff --git a/apollo4.js b/apollo4.js index c1905d1..9d41daf 100644 --- a/apollo4.js +++ b/apollo4.js @@ -3,9 +3,9 @@ const { buildSchema, GraphQLError } = require('graphql') -const { validateQuery } = require('./index') const { constraintDirectiveTypeDefs } = require('./lib/type-defs') const { gql } = require('graphql-tag') +const { validateQuery } = require('./lib/validate-query') let currentSchema diff --git a/index.js b/index.js index a702b1c..82d8312 100644 --- a/index.js +++ b/index.js @@ -1,15 +1,13 @@ const { GraphQLNonNull, GraphQLList, - TypeInfo, - ValidationContext, - visit, - visitWithTypeInfo, separateOperations, GraphQLError, getDirectiveValues } = require('graphql') const QueryValidationVisitor = require('./lib/query-validation-visitor.js') +const { validateQuery } = require('./lib/validate-query') + const { getDirective, mapSchema, MapperKind } = require('@graphql-tools/utils') const { getConstraintTypeObject, getScalarType } = require('./lib/type-utils') const { constraintDirectiveTypeDefs, constraintDirectiveTypeDefsObj } = require('./lib/type-defs') @@ -173,28 +171,6 @@ function constraintDirectiveDocumentation (options) { }) } -function validateQuery (schema, query, variables, operationName, pluginOptions = {}) { - const typeInfo = new TypeInfo(schema) - - const errors = [] - const context = new ValidationContext( - schema, - query, - typeInfo, - (error) => errors.push(error) - ) - - const visitor = new QueryValidationVisitor(context, { - variables, - operationName, - pluginOptions - }) - - visit(query, visitWithTypeInfo(typeInfo, visitor)) - - return errors -} - function createApolloQueryValidationPlugin ({ schema }, options = {}) { return { async requestDidStart () { diff --git a/lib/error.js b/lib/error.js index 1a1a78a..d10e845 100644 --- a/lib/error.js +++ b/lib/error.js @@ -1,4 +1,6 @@ module.exports = class ConstraintDirectiveError extends Error { + ; + constructor (fieldName, message, context) { super(message) this.name = this.constructor.name @@ -7,5 +9,6 @@ module.exports = class ConstraintDirectiveError extends Error { this.code = 'ERR_GRAPHQL_CONSTRAINT_VALIDATION' this.fieldName = fieldName this.context = context + this.originalError = undefined } } diff --git a/lib/type-utils.js b/lib/type-utils.js index f1053b8..64f0abc 100644 --- a/lib/type-utils.js +++ b/lib/type-utils.js @@ -48,7 +48,7 @@ function getScalarType (fieldConfig) { } else if (isNonNullType(fieldConfig) && isScalarType(fieldConfig.ofType)) { return { scalarType: fieldConfig.ofType, scalarNotNull: true } } else if (isNonNullType(fieldConfig)) { - return { ...getScalarType(fieldConfig.ofType.ofType), list: true, listNotNull: true } + return { ...getScalarType(fieldConfig.ofType), list: true, listNotNull: true } } else { throw new Error(`Not a valid scalar type: ${fieldConfig.toString()}`) } diff --git a/lib/validate-query.js b/lib/validate-query.js new file mode 100644 index 0000000..a227c71 --- /dev/null +++ b/lib/validate-query.js @@ -0,0 +1,31 @@ +const { + TypeInfo, + ValidationContext, + visit, + visitWithTypeInfo +} = require('graphql') +const QueryValidationVisitor = require('./query-validation-visitor.js') + +function validateQuery (schema, query, variables, operationName, pluginOptions = {}) { + const typeInfo = new TypeInfo(schema) + + const errors = [] + const context = new ValidationContext( + schema, + query, + typeInfo, + (error) => errors.push(error) + ) + + const visitor = new QueryValidationVisitor(context, { + variables, + operationName, + pluginOptions + }) + + visit(query, visitWithTypeInfo(typeInfo, visitor)) + + return errors +} + +module.exports = { validateQuery } diff --git a/package-lock.json b/package-lock.json index 361b2bf..38138e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,8 @@ "mocha": "10.7.3", "nyc": "15.1.0", "standard": "16.0.4", - "supertest": "6.3.4" + "supertest": "6.3.4", + "typescript": "^5.5.3" }, "engines": { "node": ">=14.0.0" @@ -7101,6 +7102,19 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typescript": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -12945,6 +12959,12 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "dev": true + }, "unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", diff --git a/package.json b/package.json index 526bf52..0bdc903 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "test-apollo-plugin": "standard && nyc --reporter=html --reporter=text --reporter=lcov mocha test/**/testsuite-apollo-plugin.js", "test-apollo4-plugin": "standard && nyc --reporter=html --reporter=text --reporter=lcov mocha test/**/testsuite-apollo4-plugin.js", "test-envelop-plugin": "standard && nyc --reporter=html --reporter=text --reporter=lcov mocha test/**/testsuite-envelop-plugin.js", + "test-typescript": "tsc --project tsconfig.apollo4.json && tsc --project tsconfig.apollo.json", "test-validation-rule-express-graphql": "standard && nyc --reporter=html --reporter=text --reporter=lcov mocha test/**/testsuite-validation-rule-express-graphql.js" }, "author": "James Mortemore (jamesmortemore@gmail.com)", @@ -34,8 +35,9 @@ "constraint" ], "devDependencies": { - "apollo-server-express": "3.13.0", "@apollo/server": "4.9.5", + "@graphql-yoga/node": "2.13.13", + "apollo-server-express": "3.13.0", "coveralls": "3.1.1", "express": "4.21.0", "graphql": "16.6.0", @@ -43,7 +45,7 @@ "nyc": "15.1.0", "standard": "16.0.4", "supertest": "6.3.4", - "@graphql-yoga/node": "2.13.13" + "typescript": "^5.5.3" }, "dependencies": { "@graphql-tools/schema": "^9.0.0", diff --git a/test/typescript/apollo.ts b/test/typescript/apollo.ts new file mode 100644 index 0000000..1f9b5fe --- /dev/null +++ b/test/typescript/apollo.ts @@ -0,0 +1 @@ +import { constraintDirective, constraintDirectiveTypeDefs } from 'graphql-constraint-directive'; diff --git a/test/typescript/apollo4.ts b/test/typescript/apollo4.ts new file mode 100644 index 0000000..60f54b1 --- /dev/null +++ b/test/typescript/apollo4.ts @@ -0,0 +1,3 @@ +import { constraintDirectiveTypeDefs, createApollo4QueryValidationPlugin } from 'graphql-constraint-directive/apollo4'; + +export { constraintDirectiveTypeDefs, createApollo4QueryValidationPlugin }; \ No newline at end of file diff --git a/test/typescript/error.ts b/test/typescript/error.ts new file mode 100644 index 0000000..abf7e4d --- /dev/null +++ b/test/typescript/error.ts @@ -0,0 +1,3 @@ +const errorFn: () => string = { + +} \ No newline at end of file diff --git a/tsconfig.apollo.json b/tsconfig.apollo.json new file mode 100644 index 0000000..f2f8b79 --- /dev/null +++ b/tsconfig.apollo.json @@ -0,0 +1,32 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": ".", + "rootDir": ".", + "declaration": false, + "moduleResolution": "node", + "noEmit": true, + "importHelpers": true, + "target": "es2015", + "module": "esnext", + "lib": [ + "es2020", + ], + "checkJs": true, + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "paths": { + "graphql-constraint-directive": [ + "index.js", + "index.d.ts", + ] + } + }, + "include": [ + "./test/typescript/apollo.ts" + ], + "exclude": [ + "node_modules", + "tmp" + ] +} \ No newline at end of file diff --git a/tsconfig.apollo4.json b/tsconfig.apollo4.json new file mode 100644 index 0000000..e464e21 --- /dev/null +++ b/tsconfig.apollo4.json @@ -0,0 +1,35 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": ".", + "rootDir": ".", + "declaration": false, + "moduleResolution": "node", + "noEmit": true, + "importHelpers": true, + "target": "es2015", + "module": "esnext", + "lib": [ + "es2020", + ], + "checkJs": true, + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "paths": { + "graphql-constraint-directive/apollo4": [ + "apollo4.js", + "apollo4.d.ts", + ], + "apollo-server-errors": [ + "./test/typescript/error.ts" + ] + } + }, + "include": [ + "./test/typescript/apollo4.ts" + ], + "exclude": [ + "node_modules", + "tmp" + ] +} \ No newline at end of file