Skip to content

Commit

Permalink
Add directive#toString() & directive#toDocumentNode()
Browse files Browse the repository at this point in the history
A recent change to Apollo Server's type definitions caused Typescript to complain about how we were exporting directive type definitions from this library. To solve this, two new methods were added to generate the correct type declarations while also satisfying the type issues with the directive class.

This results in a breaking change and the documentation has been updated to reflect the new requirements for adding directives to a schema.
  • Loading branch information
Saeris committed Oct 14, 2020
1 parent db2a4ac commit 470f5e0
Show file tree
Hide file tree
Showing 15 changed files with 1,872 additions and 4,493 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ module.exports = {

// ECMAScript 6
"arrow-body-style": [2, `as-needed`],
"arrow-parens": [2, `as-needed`],
"arrow-parens": [2, `always`],
"arrow-spacing": [2, { before: true, after: true }],
"constructor-super": 0,
"generator-star-spacing": [2, { before: true, after: false }],
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,18 @@ const server = new ApolloServer({
typeDefs: [
...typeDefs,
// Map over each directive and get it's type declaration to add them to your schema
...Object.values(Directives).map(directive => directive.declaration())
//
// Apollo prefers not to mix and match types for graphql type definitions, so typeDefs
// should be either string[] or DocumentNode[]. We expose two helper methods to get the
// correct type declaration.
//
// - use directive#toString() when your types are plain strings
// - use directive#toDocumentNode() when your types are defined using graphql-tag
...Object.values(Directives).map((directive) => directive.toDocumentNode())
// Follow the same pattern to get type declarations for groups
// ...Object.values(Strings).map(directive = directive.declaration())
// ...Object.values(Strings).map(directive = directive.toDocumentNode())
// Or for individual directives, just call the getter directly
// formatDate.declaration()
// formatDate.toDocumentNode()
],
resolvers,
// Add the directives to your schemaDirectives map
Expand Down
4 changes: 2 additions & 2 deletions example/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const endpoint = host
? `https://${host.replace(`sse-sandbox-`, ``)}.sse.codesandbox.io`
: `localhost:9000`

const directives = Object.values(schemaDirectives).map(directive =>
directive.declaration()
const directives = Object.values(schemaDirectives).map((directive) =>
directive.toDocumentNode()
)

const server = new ApolloServer({
Expand Down
43 changes: 22 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@
"version": "npm run build"
},
"dependencies": {
"apollo-server": "^2.18.2",
"date-fns": "2.16.1",
"dinero.js": "^1.8.1",
"graphql-tools": "^6.2.1",
"libphonenumber-js": "^1.7.57",
"libphonenumber-js": "^1.8.4",
"lodash": "^4.17.20",
"mathjs": "^7.2.0",
"mathjs": "^7.5.1",
"numeral": "2.0.6"
},
"devDependencies": {
Expand All @@ -56,38 +56,39 @@
"@pika/plugin-standard-pkg": "^0.9.2",
"@types/dinero.js": "^1.6.5",
"@types/graphql": "14.5.0",
"@types/hapi__joi": "^17.1.4",
"@types/jest": "^26.0.13",
"@types/jest": "^26.0.14",
"@types/lodash-es": "^4.17.3",
"@types/mathjs": "^6.0.5",
"@types/node": "^14.6.4",
"@types/node": "^14.11.8",
"@types/numeral": "^0.0.28",
"@typescript-eslint/eslint-plugin": "^4.1.0",
"@typescript-eslint/parser": "^4.1.0",
"apollo-server": "^2.17.0",
"@typescript-eslint/eslint-plugin": "^4.4.1",
"@typescript-eslint/parser": "^4.4.1",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.3.0",
"codecov": "^3.7.2",
"babel-jest": "^26.5.2",
"codecov": "^3.8.0",
"core-js": "3.6.5",
"eslint": "^7.8.1",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-jest": "^24.0.0",
"eslint": "^7.11.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.1.0",
"eslint-plugin-promise": "^4.2.1",
"esm": "3.2.25",
"fiddly": "^0.9.1",
"graphql": "15.3.0",
"graphql-tag": "^2.11.0",
"jest": "^26.4.2",
"lint-staged": "^10.3.0",
"nodemon": "2.0.4",
"jest": "^26.5.3",
"lint-staged": "^10.4.0",
"nodemon": "2.0.5",
"npm-run-all": "^4.1.5",
"prettier": "^2.1.1",
"typescript": "^4.0.2"
"prettier": "^2.1.2",
"typescript": "^4.0.3"
},
"peerDependencies": {
"graphql": "^14.0.0",
"graphql-tag": "^2.10.1"
"graphql": "^15.3.0",
"graphql-tag": "^2.11.0"
},
"resolutions": {
"graphql-tools": "4.0.7"
},
"@pika/pack": {
"pipeline": [
Expand Down
30 changes: 20 additions & 10 deletions src/__TEST__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
import { SchemaDirectiveVisitor } from "graphql-tools"
import { SchemaDirectiveVisitor } from "apollo-server"
import { GraphQLDirective } from "graphql"
import { default as Directives } from "../"

describe(`bulk export`, () => {
it(`directives have directive document nodes`, () => {
describe(`directive exports`, () => {
it(`each have directive declarations`, () => {
for (const directive of Object.values(Directives)) {
expect(directive.declaration().kind).toBe(`Document`)
expect(
directive
.declaration()
.definitions.some(({ kind }) => kind === `DirectiveDefinition`)
).toBe(true)
expect(directive.getDirectiveDeclaration()).toBeInstanceOf(
GraphQLDirective
)
}
})

it(`directives are SchemaDirectiveVisitors`, () => {
it(`each converts directive declarations to strings`, () => {
for (const directive of Object.values(Directives)) {
expect(typeof directive.toString()).toBe(`string`)
}
})

it(`each converts directive declarations to Document Nodes`, () => {
for (const directive of Object.values(Directives)) {
expect(directive.toDocumentNode().kind).toBe(`Document`)
}
})

it(`each are instances of SchemaDirectiveVisitor`, () => {
for (const directive of Object.values(Directives)) {
expect(directive.prototype instanceof SchemaDirectiveVisitor).toBe(true)
}
Expand Down
59 changes: 38 additions & 21 deletions src/currencies.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,52 @@
import gql from "graphql-tag"
import { SchemaDirectiveVisitor } from "graphql-tools"
import { SchemaDirectiveVisitor } from "apollo-server"
import {
defaultFieldResolver,
GraphQLDirective,
DirectiveLocation,
GraphQLString,
GraphQLEnumType,
GraphQLField,
GraphQLArgument
} from "graphql"
import dinero from "dinero.js"
import { getTypeMap } from "./utils"
import { directiveToString, directiveToDocumentNode } from "./utils"

const roundingModeEnum = `
enum RoundingMode {
HALF_ODD
HALF_EVEN
HALF_UP
HALF_DOWN
HALF_TOWARD_ZERO
HALF_AWAY_FROM_ZERO
const RoundingMode = new GraphQLEnumType({
name: `RoundingMode`,
values: {
HALF_ODD: {},
HALF_EVEN: {},
HALF_UP: {},
HALF_DOWN: {},
HALF_TOWARD_ZERO: {},
HALF_AWAY_FROM_ZERO: {}
}
`
})

export class formatCurrency extends SchemaDirectiveVisitor {
static declaration() {
return gql`
directive @formatCurrency(
defaultFormat: String! = "$0,0.00"
defaultRoundingMode: RoundingMode! = HALF_AWAY_FROM_ZERO
) on FIELD_DEFINITION
static getDirectiveDeclaration() {
return new GraphQLDirective({
name: `formatCurrency`,
locations: [DirectiveLocation.FIELD_DEFINITION],
args: {
defaultFormat: {
type: GraphQLString,
defaultValue: `$0,0.00`
},
defaultRoundingMode: {
type: RoundingMode,
defaultValue: `HALF_AWAY_FROM_ZERO`
}
}
})
}

static toString() {
return directiveToString(this.getDirectiveDeclaration())
}

${roundingModeEnum}
`
static toDocumentNode() {
return directiveToDocumentNode(this.getDirectiveDeclaration())
}

visitFieldDefinition(field: GraphQLField<any, any, any>) {
Expand All @@ -43,7 +60,7 @@ export class formatCurrency extends SchemaDirectiveVisitor {
} as GraphQLArgument)
field.args.push({
name: `roundingMode`,
type: getTypeMap(roundingModeEnum).RoundingMode
type: RoundingMode
} as GraphQLArgument)

field.resolve = async function (
Expand Down
31 changes: 23 additions & 8 deletions src/dates.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
import gql from "graphql-tag"
import { SchemaDirectiveVisitor } from "graphql-tools"
import { SchemaDirectiveVisitor } from "apollo-server"
import {
defaultFieldResolver,
GraphQLDirective,
DirectiveLocation,
GraphQLString,
GraphQLField,
GraphQLArgument
} from "graphql"
import { default as formatter } from "date-fns/format"
import { directiveToString, directiveToDocumentNode } from "./utils"

export class formatDate extends SchemaDirectiveVisitor {
static declaration() {
return gql`
directive @formatDate(
defaultFormat: String! = "mmmm d, yyyy"
) on FIELD_DEFINITION
`
static getDirectiveDeclaration() {
return new GraphQLDirective({
name: `formatDate`,
locations: [DirectiveLocation.FIELD_DEFINITION],
args: {
defaultFormat: {
type: GraphQLString,
defaultValue: `mmmm d, yyyy`
}
}
})
}

static toString() {
return directiveToString(this.getDirectiveDeclaration())
}

static toDocumentNode() {
return directiveToDocumentNode(this.getDirectiveDeclaration())
}

visitFieldDefinition(field: GraphQLField<any, any, any>) {
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as Strings from "./strings"
import * as Units from "./units"

// Export all durectives as default
export default {
const schemaDirectives = {
...Currencies,
...Dates,
// ...Limits,
Expand All @@ -17,6 +17,8 @@ export default {
...Units
}

export default schemaDirectives

// Export namespaced groups of directives
export {
Currencies,
Expand Down
2 changes: 1 addition & 1 deletion src/limits.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SchemaDirectiveVisitor } from "graphql-tools"
import { SchemaDirectiveVisitor } from "apollo-server"
import {
GraphQLNonNull,
GraphQLScalarType,
Expand Down
31 changes: 23 additions & 8 deletions src/numbers.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
import gql from "graphql-tag"
import { SchemaDirectiveVisitor } from "graphql-tools"
import { SchemaDirectiveVisitor } from "apollo-server"
import {
defaultFieldResolver,
GraphQLDirective,
DirectiveLocation,
GraphQLString,
GraphQLField,
GraphQLArgument
} from "graphql"
import numeral from "numeral"
import { directiveToString, directiveToDocumentNode } from "./utils"

export class formatNumber extends SchemaDirectiveVisitor {
static declaration() {
return gql`
directive @formatNumber(
defaultFormat: String! = "0,0.0000"
) on FIELD_DEFINITION
`
static getDirectiveDeclaration() {
return new GraphQLDirective({
name: `formatNumber`,
locations: [DirectiveLocation.FIELD_DEFINITION],
args: {
defaultFormat: {
type: GraphQLString,
defaultValue: `0,0.0000`
}
}
})
}

static toString() {
return directiveToString(this.getDirectiveDeclaration())
}

static toDocumentNode() {
return directiveToDocumentNode(this.getDirectiveDeclaration())
}

visitFieldDefinition(field: GraphQLField<any, any, any>) {
Expand Down
Loading

0 comments on commit 470f5e0

Please sign in to comment.