Skip to content

Commit

Permalink
feat: import directives (#35)
Browse files Browse the repository at this point in the history
* Added unittest

* Update testcase

* Remove directive @FIRST on tests

* Workaground for directive import

* Using new type for Definitions

* Clean-up cloned code

* Collect directives on Node

* fix tests coverage
  • Loading branch information
giautm authored and kbrandwijk committed Jan 12, 2018
1 parent 0a69d67 commit 4f1c0a4
Show file tree
Hide file tree
Showing 16 changed files with 146 additions and 120 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Assume the following directory structure:

type A {
# test 1
first: String @first
first: String
second: Float
b: B
}
Expand Down Expand Up @@ -70,7 +70,7 @@ Running `console.log(importSchema('a.graphql'))` procudes the following output:

```graphql
type A {
first: String @first
first: String
second: Float
b: B
}
Expand Down
4 changes: 2 additions & 2 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ <h2 id="example">Example</h2>

type A {
# test 1
first: String @first
first: String
second: Float
b: B
}
Expand All @@ -110,7 +110,7 @@ <h2 id="example">Example</h2>
</code></pre>
<p>Running <code>console.log(importSchema(&#39;a.graphql&#39;))</code> procudes the following output:</p>
<pre><code class="lang-graphql">type A {
first: String @first
first: String
second: Float
b: B
}
Expand Down
2 changes: 1 addition & 1 deletion fixtures/circular/a.graphql
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# import * from "b.graphql"

type A {
first: String @first
first: String
second: Float
b: B
}
5 changes: 5 additions & 0 deletions fixtures/directive/a.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import * from 'd.graphql'
type A {
first: String @upper
second: String @withB @deprecated
}
1 change: 1 addition & 0 deletions fixtures/directive/b.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scalar B
3 changes: 3 additions & 0 deletions fixtures/directive/d.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#import B from 'b.graphql'
directive @upper on FIELD_DEFINITION
directive @withB(argB: B) on FIELD_DEFINITION
2 changes: 1 addition & 1 deletion fixtures/enums/a.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

type A {
# test 1
first: String @first
first: String
second: Float
b: B
}
2 changes: 1 addition & 1 deletion fixtures/field-types/a.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

type A {
# test 1
first: String @first
first: String
second: Float
b: B
}
2 changes: 1 addition & 1 deletion fixtures/import-all/a.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

type A {
# test 1
first: String @first
first: String
second: Float
b: B
}
2 changes: 1 addition & 1 deletion fixtures/input-types/a.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

type A {
# test 1
first(b: B): String @first
first(b: B): String
second: Float
}
2 changes: 1 addition & 1 deletion fixtures/interfaces/a.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

type A implements B {
# test 1
first: String @first
first: String
second: Float
}
2 changes: 1 addition & 1 deletion fixtures/related-types/a.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

type A {
# test 1
first: String @first
first: String
second: Float
b: B
}
3 changes: 3 additions & 0 deletions fixtures/type-not-found/g.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type U {
hello: String! @first
}
148 changes: 69 additions & 79 deletions src/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@ import {
InputObjectTypeDefinitionNode,
TypeNode,
NamedTypeNode,
DirectiveNode,
DirectiveDefinitionNode,
InputValueDefinitionNode,
FieldDefinitionNode,
} from 'graphql'

const builtinTypes = ['String', 'Float', 'Int', 'Boolean', 'ID']

const builtinDirectives = ['deprecated', 'skip','include']

export type ValidDefinitionNode =
| DirectiveDefinitionNode
| TypeDefinitionNode

export interface DefinitionMap {
[key: string]: TypeDefinitionNode
[key: string]: ValidDefinitionNode
}

/**
Expand All @@ -24,10 +34,10 @@ export interface DefinitionMap {
* @returns Final collection of type definitions for the resulting schema
*/
export function completeDefinitionPool(
allDefinitions: TypeDefinitionNode[],
definitionPool: TypeDefinitionNode[],
newTypeDefinitions: TypeDefinitionNode[],
): TypeDefinitionNode[] {
allDefinitions: ValidDefinitionNode[],
definitionPool: ValidDefinitionNode[],
newTypeDefinitions: ValidDefinitionNode[],
): ValidDefinitionNode[] {
const visitedDefinitions: { [name: string]: boolean } = {}
while (newTypeDefinitions.length > 0) {
const schemaMap: DefinitionMap = keyBy(allDefinitions, d => d.name.value)
Expand Down Expand Up @@ -65,52 +75,24 @@ export function completeDefinitionPool(
* @returns All relevant type definitions to add to the final schema
*/
function collectNewTypeDefinitions(
allDefinitions: TypeDefinitionNode[],
definitionPool: TypeDefinitionNode[],
newDefinition: TypeDefinitionNode,
allDefinitions: ValidDefinitionNode[],
definitionPool: ValidDefinitionNode[],
newDefinition: ValidDefinitionNode,
schemaMap: DefinitionMap,
): TypeDefinitionNode[] {
let newTypeDefinitions: TypeDefinitionNode[] = []
): ValidDefinitionNode[] {
let newTypeDefinitions: ValidDefinitionNode[] = []

if (newDefinition.kind === 'InputObjectTypeDefinition') {
newDefinition.fields.forEach(field => {
const namedType = getNamedType(field.type)
const typeName = namedType.name.value
if (newDefinition.kind !== 'DirectiveDefinition') {
newDefinition.directives.forEach(collectDirective)
}

// collect missing argument input types
if (
!definitionPool.some(d => d.name.value === typeName) &&
!builtinTypes.includes(typeName)
) {
const argTypeMatch = schemaMap[typeName]
if (!argTypeMatch) {
throw new Error(
`Field ${field.name.value}: Couldn't find type ${typeName} in any of the schemas.`,
)
}
newTypeDefinitions.push(argTypeMatch)
}
})
if (newDefinition.kind === 'InputObjectTypeDefinition') {
newDefinition.fields.forEach(collectNode)
}

if (newDefinition.kind === 'InterfaceTypeDefinition') {
const interfaceName = newDefinition.name.value
newDefinition.fields.forEach(field => {
const namedType = getNamedType(field.type)
const typeName = namedType.name.value
if (
!definitionPool.some(d => d.name.value === typeName) &&
!builtinTypes.includes(typeName)
) {
const schemaType = schemaMap[typeName] as ObjectTypeDefinitionNode
if (!schemaType) {
throw new Error(
`Field ${field.name.value}: Couldn't find type ${typeName} in any of the schemas.`,
)
}
newTypeDefinitions.push(schemaType)
}
})
newDefinition.fields.forEach(collectNode)

const interfaceImplementations = allDefinitions.filter(
d =>
Expand Down Expand Up @@ -150,46 +132,54 @@ function collectNewTypeDefinitions(

// iterate over all fields
newDefinition.fields.forEach(field => {
const namedType = getNamedType(field.type)
const typeName = namedType.name.value

collectNode(field)
// collect missing argument input types
field.arguments.forEach(argument => {
const argType = getNamedType(argument.type)
const argTypeName = argType.name.value
if (
!definitionPool.some(d => d.name.value === argTypeName) &&
!builtinTypes.includes(argTypeName)
) {
const argTypeMatch = schemaMap[argTypeName]
if (!argTypeMatch) {
throw new Error(
`Field ${field.name.value}: Couldn't find type ${
argTypeName
} in any of the schemas.`,
)
}
newTypeDefinitions.push(argTypeMatch)
}
})

// collect missing field types
if (
!definitionPool.some(d => d.name.value === typeName) &&
!builtinTypes.includes(typeName)
) {
const schemaType = schemaMap[typeName] as ObjectTypeDefinitionNode
if (!schemaType) {
throw new Error(
`Field ${field.name.value}: Couldn't find type ${typeName} in any of the schemas.`,
)
}
newTypeDefinitions.push(schemaType)
}
field.arguments.forEach(collectNode)
})
}

return newTypeDefinitions

function collectNode(node: FieldDefinitionNode | InputValueDefinitionNode) {
const nodeType = getNamedType(node.type)
const nodeTypeName = nodeType.name.value

// collect missing argument input types
if (
!definitionPool.some(d => d.name.value === nodeTypeName) &&
!builtinTypes.includes(nodeTypeName)
) {
const argTypeMatch = schemaMap[nodeTypeName]
if (!argTypeMatch) {
throw new Error(
`Field ${node.name.value}: Couldn't find type ${nodeTypeName} in any of the schemas.`,
)
}
newTypeDefinitions.push(argTypeMatch)
}

node.directives.forEach(collectDirective)
}

function collectDirective(directive: DirectiveNode) {
const directiveName = directive.name.value
if (
!definitionPool.some(d => d.name.value === directiveName) &&
!builtinDirectives.includes(directiveName)
) {
const directive = schemaMap[directiveName] as DirectiveDefinitionNode
if (!directive) {
throw new Error(
`Directive ${directiveName}: Couldn't find type ${
directiveName
} in any of the schemas.`,
)
}
directive.arguments.forEach(collectNode)

newTypeDefinitions.push(directive)
}
}
}

/**
Expand Down
Loading

0 comments on commit 4f1c0a4

Please sign in to comment.