Created
September 10, 2021 14:35
-
-
Save mscharley/6a5b3f708b369de5ace5d92efe093e31 to your computer and use it in GitHub Desktop.
Simple merging algorithm for multiple GraphQL schema files into AppSync
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | |
if (k2 === undefined) k2 = k; | |
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | |
}) : (function(o, m, k, k2) { | |
if (k2 === undefined) k2 = k; | |
o[k2] = m[k]; | |
})); | |
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | |
Object.defineProperty(o, "default", { enumerable: true, value: v }); | |
}) : function(o, v) { | |
o["default"] = v; | |
}); | |
var __importStar = (this && this.__importStar) || function (mod) { | |
if (mod && mod.__esModule) return mod; | |
var result = {}; | |
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | |
__setModuleDefault(result, mod); | |
return result; | |
}; | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
exports.loadSchemas = void 0; | |
const appSync = __importStar(require("@aws-cdk/aws-appsync")); | |
const graphql_1 = require("graphql"); | |
const path_1 = require("path"); | |
const fs_1 = require("fs"); | |
const loop = (documents, schema) => { | |
if (documents.length <= 0) { | |
return schema; | |
} | |
const types = schema.getTypeMap(); | |
// This iteration, only include documents that either only add new types or extend existing types. | |
const documentsToInclude = documents.filter((d) => d.definitions.find((def) => (def.kind === 'EnumTypeExtension' || | |
def.kind === 'InputObjectTypeExtension' || | |
def.kind === 'InterfaceTypeExtension' || | |
def.kind === 'ObjectTypeExtension' || | |
def.kind === 'ScalarTypeExtension' || | |
def.kind === 'UnionTypeExtension') && | |
!(def.name.value in types)) == null); | |
if (documentsToInclude.length === 0) { | |
const dependencies = JSON.stringify(documents.map((d) => { var _a; return (_a = d.loc) === null || _a === void 0 ? void 0 : _a.source.name; })); | |
throw new Error(`Unable to load schemas, possible circular dependency found: ${dependencies}`); | |
} | |
const remainingDocuments = documents.filter((v) => !documentsToInclude.includes(v)); | |
return loop(remainingDocuments, documentsToInclude.reduce((acc, v) => (0, graphql_1.extendSchema)(acc, v), schema)); | |
}; | |
/** | |
* Loads a set of graphql schema files into an AppSync schema. | |
* | |
* The order of graphql input schemas doesn't matter, this will load them in an order that just works. | |
* | |
* @param pluginDirs A list of folders with schema files in them. | |
* @returns | |
*/ | |
const loadSchemas = (pluginDirs) => { | |
const partialSchemas = pluginDirs.map((f) => (0, graphql_1.parse)(new graphql_1.Source((0, fs_1.readFileSync)((0, path_1.join)(f, 'schema.graphql')).toString('utf-8'), (0, path_1.join)(f, 'schema.graphql')))); | |
const gqlSchema = loop(partialSchemas, new graphql_1.GraphQLSchema({})); | |
const schema = new appSync.Schema(); | |
schema.addToSchema('schema { query: Query }'); | |
schema.addToSchema((0, graphql_1.printSchema)(gqlSchema, { | |
commentDescriptions: true, | |
})); | |
return schema; | |
}; | |
exports.loadSchemas = loadSchemas; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as appSync from '@aws-cdk/aws-appsync'; | |
import { | |
extendSchema, | |
GraphQLSchema, | |
parse as parseGql, | |
printSchema, | |
Source, | |
} from 'graphql'; | |
import type { DocumentNode } from 'graphql'; | |
import { join } from 'path'; | |
import { readFileSync } from 'fs'; | |
const loop = ( | |
documents: DocumentNode[], | |
schema: GraphQLSchema, | |
): GraphQLSchema => { | |
if (documents.length <= 0) { | |
return schema; | |
} | |
const types = schema.getTypeMap(); | |
// This iteration, only include documents that either only add new types or extend existing types. | |
const documentsToInclude = documents.filter( | |
(d) => | |
d.definitions.find( | |
(def) => | |
(def.kind === 'EnumTypeExtension' || | |
def.kind === 'InputObjectTypeExtension' || | |
def.kind === 'InterfaceTypeExtension' || | |
def.kind === 'ObjectTypeExtension' || | |
def.kind === 'ScalarTypeExtension' || | |
def.kind === 'UnionTypeExtension') && | |
!(def.name.value in types), | |
) == null, | |
); | |
if (documentsToInclude.length === 0) { | |
const dependencies = JSON.stringify( | |
documents.map((d) => d.loc?.source.name), | |
); | |
throw new Error( | |
`Unable to load schemas, possible circular dependency found: ${dependencies}`, | |
); | |
} | |
const remainingDocuments = documents.filter( | |
(v) => !documentsToInclude.includes(v), | |
); | |
return loop( | |
remainingDocuments, | |
documentsToInclude.reduce((acc, v) => extendSchema(acc, v), schema), | |
); | |
}; | |
/** | |
* Loads a set of graphql schema files into an AppSync schema. | |
* | |
* The order of graphql input schemas doesn't matter, this will load them in an order that just works. | |
* | |
* @param pluginDirs A list of folders with schema files in them. | |
* @returns | |
*/ | |
export const loadSchemas = (pluginDirs: string[]): appSync.Schema => { | |
const partialSchemas = pluginDirs.map((f) => | |
parseGql( | |
new Source( | |
readFileSync(join(f, 'schema.graphql')).toString('utf-8'), | |
join(f, 'schema.graphql'), | |
), | |
), | |
); | |
const gqlSchema = loop(partialSchemas, new GraphQLSchema({})); | |
const schema = new appSync.Schema(); | |
schema.addToSchema('schema { query: Query }'); | |
schema.addToSchema( | |
printSchema(gqlSchema, { | |
commentDescriptions: true, | |
}), | |
); | |
return schema; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment