UNPKG

@aws-amplify/graphql-transformer-core

Version:

A framework to transform from GraphQL SDL to AWS CloudFormation.

507 lines 19.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TransformerOutput = exports.objectExtension = exports.blankObject = void 0; const graphql_1 = require("graphql"); const strip_directives_1 = require("../utils/strip-directives"); const defaultSchema_1 = require("../utils/defaultSchema"); const AMPLIFY = 'AMPLIFY'; function blankObject(name) { return { kind: 'ObjectTypeDefinition', name: { kind: 'Name', value: name, }, fields: [], directives: [], interfaces: [], }; } exports.blankObject = blankObject; function objectExtension(name, fields = []) { return { kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION, name: { kind: 'Name', value: name, }, fields, directives: [], interfaces: [], }; } exports.objectExtension = objectExtension; class TransformerOutput { constructor(inputDocument) { this.nodeMap = {}; const extensionNodes = []; for (const inputDef of inputDocument.definitions) { switch (inputDef.kind) { case graphql_1.Kind.OBJECT_TYPE_DEFINITION: case graphql_1.Kind.SCALAR_TYPE_DEFINITION: case graphql_1.Kind.INTERFACE_TYPE_DEFINITION: case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION: case graphql_1.Kind.ENUM_TYPE_DEFINITION: case graphql_1.Kind.UNION_TYPE_DEFINITION: { const typeDef = inputDef; if (this.isAmplifyInput(typeDef.name.value)) break; if (!this.getType(typeDef.name.value)) { this.addType(typeDef); } break; } case graphql_1.Kind.SCHEMA_DEFINITION: if (!this.getSchema()) { const typeDef = inputDef; this.putSchema(typeDef); } break; case graphql_1.Kind.OBJECT_TYPE_EXTENSION: case graphql_1.Kind.ENUM_TYPE_EXTENSION: case graphql_1.Kind.UNION_TYPE_EXTENSION: case graphql_1.Kind.INTERFACE_TYPE_EXTENSION: case graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION: extensionNodes.push(inputDef); break; default: } } for (const ext of extensionNodes) { switch (ext.kind) { case graphql_1.Kind.OBJECT_TYPE_EXTENSION: this.addObjectExtension(ext); break; case graphql_1.Kind.INTERFACE_TYPE_EXTENSION: this.addInterfaceExtension(ext); break; case graphql_1.Kind.UNION_TYPE_EXTENSION: this.addUnionExtension(ext); break; case graphql_1.Kind.ENUM_TYPE_EXTENSION: this.addEnumExtension(ext); break; case graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION: this.addInputExtension(ext); break; default: continue; } } if (!this.getSchema()) { this.putSchema(defaultSchema_1.DEFAULT_SCHEMA_DEFINITION); } } getTypeDefinitionsOfKind(kind) { const typeDefs = []; for (const key of Object.keys(this.nodeMap)) { const definition = this.nodeMap[key]; if (definition.kind === kind) { typeDefs.push(definition); } } return typeDefs; } putSchema(obj) { this.nodeMap.__schema = obj; } getSchema() { return this.nodeMap.__schema; } getQueryTypeName() { const schemaNode = this.getSchema(); const queryTypeName = schemaNode.operationTypes.find((op) => op.operation === 'query'); if (queryTypeName && queryTypeName.type && queryTypeName.type.name) { return queryTypeName.type.name.value; } } getQuery() { const queryTypeName = this.getQueryTypeName(); if (queryTypeName) { return this.nodeMap[queryTypeName]; } } addDefaultQuery() { this.addOperationType(defaultSchema_1.DEFAULT_QUERY_OPERATION); } ensureQuery() { if (!this.getQueryTypeName()) { this.addDefaultQuery(); } } getMutationTypeName() { const schemaNode = this.getSchema(); const mutationTypeName = schemaNode.operationTypes.find((op) => op.operation === 'mutation'); if (mutationTypeName && mutationTypeName.type && mutationTypeName.type.name) { return mutationTypeName.type.name.value; } } getMutation() { const mutationTypeName = this.getMutationTypeName(); if (mutationTypeName) { return this.nodeMap[mutationTypeName]; } } addDefaultMutation() { this.addOperationType(defaultSchema_1.DEFAULT_MUTATION_OPERATION); } ensureMutation() { if (!this.getMutationTypeName()) { this.addDefaultMutation(); } } getSubscriptionTypeName() { const schemaNode = this.getSchema(); const subscriptionTypeName = schemaNode.operationTypes.find((op) => op.operation === 'subscription'); if (subscriptionTypeName && subscriptionTypeName.type && subscriptionTypeName.type.name) { return subscriptionTypeName.type.name.value; } } getSubscription() { const subscriptionTypeName = this.getSubscriptionTypeName(); if (subscriptionTypeName) { return this.nodeMap[subscriptionTypeName]; } } addDefaultSubscription() { this.addOperationType(defaultSchema_1.DEFAULT_SUBSCRIPTION_OPERATION); } ensureSubscription() { if (!this.getSubscriptionTypeName()) { this.addDefaultSubscription(); } } addType(obj) { if (this.nodeMap[obj.name.value]) { throw new Error(`Conflicting type '${obj.name.value}' found.`); } this.nodeMap[obj.name.value] = obj; } putType(obj) { this.nodeMap[obj.name.value] = obj; } getType(name) { return this.nodeMap[name]; } hasType(name) { return name in this.nodeMap; } addOperationType(operation) { const schemaNode = this.getSchema(); if (schemaNode.operationTypes.find((op) => op.operation === operation.operation)) { throw new Error(`Conflicting ${operation.operation} operation found.`); } else { const updatedSchema = TransformerOutput.makeSchema([...schemaNode.operationTypes, operation]); this.putSchema(updatedSchema); } } addObject(obj) { if (this.nodeMap[obj.name.value]) { throw new Error(`Conflicting type '${obj.name.value}' found.`); } this.nodeMap[obj.name.value] = obj; } updateObject(obj) { if (!this.nodeMap[obj.name.value]) { throw new Error(`Type ${obj.name.value} does not exist.`); } this.nodeMap[obj.name.value] = obj; } getObject(name) { if (this.nodeMap[name]) { const node = this.nodeMap[name]; if (node.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION) { return node; } } } addUnion(obj) { if (this.nodeMap[obj.name.value]) { throw new Error(`Conflicting union '${obj.name.value}' found.`); } this.nodeMap[obj.name.value] = obj; } addQueryFields(fields) { this.ensureQuery(); const queryTypeName = this.getQueryTypeName(); if (queryTypeName) { if (!this.getType(queryTypeName)) { this.addType(blankObject(queryTypeName)); } let queryType = objectExtension(queryTypeName, fields); this.addObjectExtension(queryType); } } addMutationFields(fields) { this.ensureMutation(); const mutationTypeName = this.getMutationTypeName(); if (mutationTypeName) { if (!this.getType(mutationTypeName)) { this.addType(blankObject(mutationTypeName)); } let mutationType = objectExtension(mutationTypeName, fields); this.addObjectExtension(mutationType); } } addSubscriptionFields(fields) { this.ensureSubscription(); const subscriptionTypeName = this.getSubscriptionTypeName(); if (subscriptionTypeName) { if (!this.getType(subscriptionTypeName)) { this.addType(blankObject(subscriptionTypeName)); } let subscriptionType = objectExtension(subscriptionTypeName, fields); this.addObjectExtension(subscriptionType); } } addObjectExtension(obj) { if (!this.nodeMap[obj.name.value]) { throw new Error(`Cannot extend nonexistent type '${obj.name.value}'.`); } const oldNode = this.getObject(obj.name.value); if (!oldNode) { throw new Error('Failure adding object extension'); } const newDirs = []; const oldDirs = (oldNode === null || oldNode === void 0 ? void 0 : oldNode.directives) || []; if (obj.directives) { for (const newDir of obj.directives) { if (Boolean(oldDirs.find((d) => d.name.value === newDir.name.value)) === false) { newDirs.push(newDir); } } } const mergedDirs = [...oldDirs, ...newDirs]; const oldFields = (oldNode === null || oldNode === void 0 ? void 0 : oldNode.fields) || []; const oldFieldMap = oldFields.reduce((acc, field) => ({ ...acc, [field.name.value]: field, }), {}); const newFields = obj.fields || []; const mergedFields = [...oldFields]; for (const newField of newFields) { if (oldFieldMap[newField.name.value]) { throw new Error(`Object type extension '${obj.name.value}' cannot redeclare field ${newField.name.value}`); } mergedFields.push(newField); } const oldInterfaces = (oldNode === null || oldNode === void 0 ? void 0 : oldNode.interfaces) || []; const oldInterfaceMap = oldInterfaces.reduce((acc, field) => ({ ...acc, [field.name.value]: field, }), {}); const newInterfaces = obj.interfaces || []; const mergedInterfaces = [...oldInterfaces]; for (const newInterface of newInterfaces) { if (oldInterfaceMap[newInterface.name.value]) { throw new Error(`Object type extension '${obj.name.value}' cannot redeclare interface ${newInterface.name.value}`); } mergedInterfaces.push(newInterface); } this.nodeMap[oldNode.name.value] = { ...oldNode, interfaces: mergedInterfaces, directives: mergedDirs, fields: mergedFields, }; } addInputExtension(obj) { if (!this.nodeMap[obj.name.value]) { throw new Error(`Cannot extend nonexistent input '${obj.name.value}'.`); } const oldNode = this.getType(obj.name.value); const newDirs = obj.directives || []; const oldDirs = oldNode.directives || []; const mergedDirs = [...oldDirs, ...newDirs]; const oldFields = oldNode.fields || []; const oldFieldMap = oldFields.reduce((acc, field) => ({ ...acc, [field.name.value]: field, }), {}); const newFields = obj.fields || []; const mergedFields = [...oldFields]; for (const newField of newFields) { if (oldFieldMap[newField.name.value]) { throw new Error(`Input object type extension '${obj.name.value}' cannot redeclare field ${newField.name.value}`); } mergedFields.push(newField); } this.nodeMap[oldNode.name.value] = { ...oldNode, directives: mergedDirs, fields: mergedFields, }; } addInterfaceExtension(obj) { if (!this.nodeMap[obj.name.value]) { throw new Error(`Cannot extend nonexistent interface '${obj.name.value}'.`); } const oldNode = this.getType(obj.name.value); const newDirs = obj.directives || []; const oldDirs = oldNode.directives || []; const mergedDirs = [...oldDirs, ...newDirs]; const oldFields = oldNode.fields || []; const oldFieldMap = oldFields.reduce((acc, field) => ({ ...acc, [field.name.value]: field, }), {}); const newFields = obj.fields || []; const mergedFields = [...oldFields]; for (const newField of newFields) { if (oldFieldMap[newField.name.value]) { throw new Error(`Interface type extension '${obj.name.value}' cannot redeclare field ${newField.name.value}`); } mergedFields.push(newField); } this.nodeMap[oldNode.name.value] = { ...oldNode, directives: mergedDirs, fields: mergedFields, }; } addUnionExtension(obj) { if (!this.nodeMap[obj.name.value]) { throw new Error(`Cannot extend nonexistent union '${obj.name.value}'.`); } const oldNode = this.getType(obj.name.value); const newDirs = obj.directives || []; const oldDirs = oldNode.directives || []; const mergedDirs = [...oldDirs, ...newDirs]; const oldTypes = oldNode.types || []; const oldTypeMap = oldTypes.reduce((acc, type) => ({ ...acc, [type.name.value]: true, }), {}); const newTypes = obj.types || []; const mergedFields = [...oldTypes]; for (const newType of newTypes) { if (oldTypeMap[newType.name.value]) { throw new Error(`Union type extension '${obj.name.value}' cannot redeclare type ${newType.name.value}`); } mergedFields.push(newType); } this.nodeMap[oldNode.name.value] = { ...oldNode, directives: mergedDirs, types: mergedFields, }; } addEnumExtension(obj) { if (!this.nodeMap[obj.name.value]) { throw new Error(`Cannot extend nonexistent enum '${obj.name.value}'.`); } const oldNode = this.getType(obj.name.value); const newDirs = obj.directives || []; const oldDirs = oldNode.directives || []; const mergedDirs = [...oldDirs, ...newDirs]; const oldValues = oldNode.values || []; const oldValuesMap = oldValues.reduce((acc, type) => ({ ...acc, [type.name.value]: true, }), {}); const newValues = obj.values || []; const mergedValues = [...oldValues]; for (const newValue of newValues) { if (oldValuesMap[newValue.name.value]) { throw new Error(`Enum type extension '${obj.name.value}' cannot redeclare value ${newValue.name.value}`); } mergedValues.push(newValue); } this.nodeMap[oldNode.name.value] = { ...oldNode, directives: mergedDirs, values: mergedValues, }; } addInput(inp) { if (this.nodeMap[inp.name.value]) { throw new Error(`Conflicting input type '${inp.name.value}' found.`); } this.nodeMap[inp.name.value] = inp; } updateInput(obj) { if (!this.nodeMap[obj.name.value]) { throw new Error(`Type ${obj.name.value} does not exist.`); } this.nodeMap[obj.name.value] = obj; } getInput(name) { if (this.nodeMap[name]) { const node = this.nodeMap[name]; if (node.kind === graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION) { return node; } } } addEnum(en) { if (this.nodeMap[en.name.value]) { throw new Error(`Conflicting enum type '${en.name.value}' found.`); } this.nodeMap[en.name.value] = en; } buildSchema() { var _a, _b, _c; const mutationNode = this.getMutation(); const queryNode = this.getQuery(); const subscriptionNode = this.getSubscription(); let includeMutation = true; let includeQuery = true; let includeSubscription = true; if (!mutationNode || ((_a = mutationNode === null || mutationNode === void 0 ? void 0 : mutationNode.fields) === null || _a === void 0 ? void 0 : _a.length) === 0) { delete this.nodeMap.Mutation; includeMutation = false; } if (!queryNode || ((_b = queryNode === null || queryNode === void 0 ? void 0 : queryNode.fields) === null || _b === void 0 ? void 0 : _b.length) === 0) { delete this.nodeMap.Query; includeQuery = false; } if (!subscriptionNode || ((_c = subscriptionNode === null || subscriptionNode === void 0 ? void 0 : subscriptionNode.fields) === null || _c === void 0 ? void 0 : _c.length) === 0) { delete this.nodeMap.Subscription; includeSubscription = false; } const ops = []; if (includeQuery) { ops.push(TransformerOutput.makeOperationType('query', queryNode.name.value)); } if (includeMutation) { ops.push(TransformerOutput.makeOperationType('mutation', mutationNode.name.value)); } if (includeSubscription) { ops.push(TransformerOutput.makeOperationType('subscription', subscriptionNode.name.value)); } const schema = TransformerOutput.makeSchema(ops); this.putSchema(schema); const astSansDirectives = (0, strip_directives_1.stripDirectives)({ kind: 'Document', definitions: Object.values(this.nodeMap), }, ['aws_subscribe', 'aws_auth', 'aws_api_key', 'aws_iam', 'aws_oidc', 'aws_cognito_user_pools', 'aws_lambda', 'deprecated']); const SDL = (0, graphql_1.print)(astSansDirectives); return SDL; } static makeOperationType(operation, type) { return { kind: 'OperationTypeDefinition', operation, type: { kind: 'NamedType', name: { kind: 'Name', value: type, }, }, }; } static makeSchema(operationTypes) { return { kind: graphql_1.Kind.SCHEMA_DEFINITION, operationTypes, directives: [], }; } isAmplifyInput(inputName) { return inputName === AMPLIFY; } } exports.TransformerOutput = TransformerOutput; //# sourceMappingURL=output.js.map