Skip to content

Commit

Permalink
Merge pull request #2 from wappla/feature/typescript
Browse files Browse the repository at this point in the history
Type package
  • Loading branch information
avdb authored Jan 22, 2024
2 parents 5ca8021 + 34c27e1 commit 98e50ac
Show file tree
Hide file tree
Showing 18 changed files with 2,452 additions and 1,606 deletions.
5 changes: 4 additions & 1 deletion fixup
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ cat >dist/esm/package.json <<!EOF
{
"type": "module"
}
!EOF
!EOF

find src -name '*.d.ts' -exec cp {} dist/mjs \;
find src -name '*.d.ts' -exec cp {} dist/cjs \;
9 changes: 7 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
export default {
// Jest configuration
const config = {
testEnvironment: 'node',
coverageDirectory: 'coverage',
transform: {}
transform: {
'^.+\\.(t|j)sx?$': '@swc/jest',
},
}

export default config
3,581 changes: 2,190 additions & 1,391 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,21 @@
"devDependencies": {
"@dashdot/eslint-config-base": "^0.3.0",
"@graphql-tools/schema": "^8.5.0",
"@types/jest": "^29.5.11",
"@swc/jest": "^0.2.24",
"jest": "^29.4.3",
"ts-jest": "^29.0.5",
"@types/test-listen": "^1.1.2",
"codecov": "^3.8.3",
"eslint": "^8.19.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^26.5.3",
"graphql": "^15.8.0",
"graphql": "^16.6.0",
"graphql-request": "^4.3.0",
"graphql-upload": "^13.0.0",
"jest": "^28.1.2",
"test-listen": "^1.1.0",
"typescript": "^4.7.4"
"typescript": "^4.9.5",
"test-listen": "^1.1.0"
},
"eslintConfig": {
"extends": "@dashdot/eslint-config-base"
Expand Down
48 changes: 24 additions & 24 deletions src/GraphqlQueryStore.js → src/GraphqlQueryStore.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,70 @@
/* eslint-disable max-classes-per-file */
/* eslint-disable class-methods-use-this */
import { validate, parse, specifiedRules } from 'graphql'
import { createHash } from 'crypto'
import { GraphQLSchema, parse, specifiedRules, validate } from 'graphql'
import { compileQuery, isCompiledQuery } from 'graphql-jit'
import { createComplexityRule, simpleEstimator } from 'graphql-query-complexity'
import { createHash } from 'crypto'
import { GraphqlValidationError } from './errors'

export default class GraphqlQueryStore {
constructor(schema, {
validationRules = [],
queryComplexity: {
maximumComplexity = 1000,
defaultComplexity = 1,
schema: GraphQLSchema
validationRules: any
maximumComplexity: number
defaultComplexity: number
store: Map<string, any>

constructor(
schema: GraphQLSchema,
{
validationRules = [],
queryComplexity: { maximumComplexity = 1000, defaultComplexity = 1 } = {},
} = {}
} = {}) {
) {
this.schema = schema
this.validationRules = validationRules
this.maximumComplexity = maximumComplexity
this.defaultComplexity = defaultComplexity
this.store = new Map()
}

createId(query) {
createId(query: string) {
const hash = createHash('sha256')
hash.update(query)
return hash.digest('hex')
}

get(id) {
get(id: string) {
return this.store.get(id)
}

has(id) {
has(id: string) {
return this.store.has(id)
}

generateValidationRules(variables) {
generateValidationRules(variables: any) {
const complexityRule = createComplexityRule({
maximumComplexity: this.maximumComplexity,
estimators: [
simpleEstimator({ defaultComplexity: this.defaultComplexity })
],
estimators: [simpleEstimator({ defaultComplexity: this.defaultComplexity })],
variables,
})
return [
...specifiedRules,
complexityRule,
...this.validationRules,
]
return [...specifiedRules, complexityRule, ...this.validationRules]
}

create(query, variables) {
create(query: string, variables: any) {
const validationErrors = validate(
this.schema,
parse(query),
this.generateValidationRules(variables),
this.generateValidationRules(variables)
)
if (validationErrors.length > 0) {
throw new GraphqlValidationError('Invalid query.', null, {
errors: validationErrors
errors: validationErrors,
})
}
const compiledQuery = compileQuery(this.schema, parse(query))
if (!isCompiledQuery(compiledQuery)) {
throw new GraphqlValidationError('Invalid query.', null, {
errors: compiledQuery.errors
errors: compiledQuery.errors,
})
}
const id = this.createId(query)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { jest } from '@jest/globals'
import { makeExecutableSchema } from '@graphql-tools/schema'
import { GraphqlContextError } from '../errors'
import createGraphqlRequestHandler from '../createGraphqlRequestHandler'
import { jest } from '@jest/globals'
import GraphqlQueryStore from '../GraphqlQueryStore'
import createGraphqlRequestHandler from '../createGraphqlRequestHandler'
import { GraphqlContextError } from '../errors'
import { RequestMock, ResponseMock } from '../testing'

const SUCCESS = 200
Expand All @@ -18,7 +18,7 @@ const INTROSPECTION_QUERY = `
}
`
const CONTENT_TYPE_JSON = {
'Content-Type': 'application/json'
'Content-Type': 'application/json',
}

const name = 'test'
Expand Down Expand Up @@ -59,11 +59,12 @@ test('Test if graphqlHandler validates request body .', async () => {

test('Test if graphqlHandler catches errors on the context function.', async () => {
const queryStore = new GraphqlQueryStore(testSchema)
const context = jest.fn(() => (
new Promise((resolve, reject) => {
reject(new GraphqlContextError())
})
))
const context = jest.fn(
() =>
new Promise((resolve, reject) => {
reject(new GraphqlContextError())
})
)
const graphqlHandler = createGraphqlRequestHandler(queryStore, context)
const request = new RequestMock()
const response = new ResponseMock()
Expand All @@ -75,11 +76,12 @@ test('Test if graphqlHandler catches errors on the context function.', async ()
})

test('Test if graphqlHandler catches errors on the resolver.', async () => {
const errorResolver = jest.fn(() => (
new Promise((resolve, reject) => {
reject(new Error())
})
))
const errorResolver = jest.fn(
() =>
new Promise((resolve, reject) => {
reject(new Error())
})
)
const errorSchema = makeExecutableSchema({
typeDefs: `
type Query {
Expand All @@ -88,7 +90,7 @@ test('Test if graphqlHandler catches errors on the resolver.', async () => {
`,
resolvers: {
Query: {
error: errorResolver
error: errorResolver,
},
},
})
Expand Down Expand Up @@ -155,9 +157,7 @@ test('Test if graphqlHandler handles query variables.', async () => {
`,
resolvers: {
Query: {
test: (_, { id }) => (
id
),
test: (_, { id }) => id,
},
},
})
Expand Down Expand Up @@ -200,24 +200,24 @@ test('Test if graphqlHandler protects against too complex queries.', async () =>
resolvers: {
Query: {
post: () => ({
id: 1
id: 1,
}),
},
Post: {
author: () => ({
id: 1
id: 1,
}),
},
Author: {
post: () => ({
id: 1
id: 1,
}),
}
},
},
})
const queryComplexity = {
maximumComplexity: 10,
defaultComplexity: 5 // each field add a complexity of 5
defaultComplexity: 5, // each field add a complexity of 5
}
const queryStore = new GraphqlQueryStore(schema, { queryComplexity })
const context = jest.fn()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { makeExecutableSchema } from '@graphql-tools/schema'
import { jest } from '@jest/globals'
import { gql } from 'graphql-request'
import { makeExecutableSchema } from '@graphql-tools/schema'
import createGraphqlRequestHandler from '../createGraphqlRequestHandler'
import GraphqlQueryStore from '../GraphqlQueryStore'
import createGraphqlRequestHandler from '../createGraphqlRequestHandler'
import { GraphqlContextError } from '../errors'
import { createTestClient, createTestServer } from '../testing'

const reject = (error) => (
const reject = (error) =>
new Promise((res, rej) => {
rej(error)
})
)

const createGraphqlServer = async (schema, context = jest.fn()) => {
const store = new GraphqlQueryStore(schema)
Expand All @@ -29,7 +28,7 @@ test('if a basic query can be resolved.', async () => {
`,
resolvers: {
Query: {
name: nameResolver
name: nameResolver,
},
},
})
Expand Down Expand Up @@ -101,13 +100,15 @@ test('if an error in the context function is returned correctly.', async () => {
const name = 'test'
const message = 'error'
const error = new GraphqlContextError(message, null, {
errors: [{
message,
path: null,
extensions: {
code: GraphqlContextError.CODE
}
}]
errors: [
{
message,
path: null,
extensions: {
code: GraphqlContextError.CODE,
},
},
],
})
const nameResolver = jest.fn().mockReturnValue(name)
const context = jest.fn().mockImplementation(() => reject(error))
Expand All @@ -119,7 +120,7 @@ test('if an error in the context function is returned correctly.', async () => {
`,
resolvers: {
Query: {
name: nameResolver
name: nameResolver,
},
},
})
Expand Down
27 changes: 0 additions & 27 deletions src/createGraphqlRequestHandler.js

This file was deleted.

34 changes: 34 additions & 0 deletions src/createGraphqlRequestHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { IncomingMessage } from 'http'
import processGraphqlRequest from './processGraphqlRequest'
import { readRequestBody } from './utils'

export default function createGraphqlRequestHandler(
store: any,
context: any,
processFileUploads?: any
) {
return async (
req: IncomingMessage,
res: {
writeHead: (arg0: number, arg1?: { 'Content-Type': string } | undefined) => void
end: (arg0: string) => void
}
) => {
const { status, text, body } = await processGraphqlRequest(req, {
store,
context,
processFileUploads,
readRequestBody,
})
if (text) {
res.writeHead(status)
res.end(text)
}
if (body) {
res.writeHead(status, {
'Content-Type': 'application/json',
})
res.end(JSON.stringify(body))
}
}
}
Loading

0 comments on commit 98e50ac

Please sign in to comment.