UNPKG

graphql-transformer-core

Version:

A framework to transform from GraphQL SDL to AWS cloudFormation.

450 lines • 21.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GraphQLTransform = void 0; const graphql_1 = require("graphql"); const graphql_transformer_common_1 = require("graphql-transformer-common"); const errors_1 = require("./errors"); const TransformerContext_1 = require("./TransformerContext"); const validation_1 = require("./validation"); const TransformFormatter_1 = require("./TransformFormatter"); const util_1 = require("./util"); const FeatureFlags_1 = require("./FeatureFlags"); function isFunction(obj) { return obj && typeof obj === 'function'; } function makeSeenTransformationKey(directive, type, field, arg, index) { let key = ''; if (directive && type && field && arg) { key = `${type.name.value}.${field.name.value}.${arg.name.value}@${directive.name.value}`; } if (directive && type && field) { key = `${type.name.value}.${field.name.value}@${directive.name.value}`; } else { key = `${type.name.value}@${directive.name.value}`; } if (index !== undefined) { key += `[${index}]`; } return key; } function matchDirective(definition, directive, node) { if (!directive) { return false; } if (definition.name.value !== directive.name.value) { return false; } let isValidLocation = false; for (const location of definition.locations) { switch (location.value) { case `SCHEMA`: isValidLocation = node.kind === graphql_1.Kind.SCHEMA_DEFINITION || isValidLocation; break; case `SCALAR`: isValidLocation = node.kind === graphql_1.Kind.SCALAR_TYPE_DEFINITION || isValidLocation; break; case `OBJECT`: isValidLocation = node.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || isValidLocation; break; case `FIELD_DEFINITION`: isValidLocation = node.kind === graphql_1.Kind.FIELD_DEFINITION || isValidLocation; break; case `ARGUMENT_DEFINITION`: isValidLocation = node.kind === graphql_1.Kind.INPUT_VALUE_DEFINITION || isValidLocation; break; case `INTERFACE`: isValidLocation = node.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION || isValidLocation; break; case `UNION`: isValidLocation = node.kind === graphql_1.Kind.UNION_TYPE_DEFINITION || isValidLocation; break; case `ENUM`: isValidLocation = node.kind === graphql_1.Kind.ENUM_TYPE_DEFINITION || isValidLocation; break; case `ENUM_VALUE`: isValidLocation = node.kind === graphql_1.Kind.ENUM_VALUE_DEFINITION || isValidLocation; break; case `INPUT_OBJECT`: isValidLocation = node.kind === graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION || isValidLocation; break; case `INPUT_FIELD_DEFINITION`: isValidLocation = node.kind === graphql_1.Kind.INPUT_VALUE_DEFINITION || isValidLocation; break; } } return isValidLocation; } function matchFieldDirective(definition, directive, node) { if (definition.name.value !== directive.name.value) { return false; } let isValidLocation = false; for (const location of definition.locations) { switch (location.value) { case `FIELD_DEFINITION`: isValidLocation = node.kind === graphql_1.Kind.FIELD_DEFINITION || isValidLocation; break; } } return isValidLocation; } function matchInputFieldDirective(definition, directive, node) { if (definition.name.value !== directive.name.value) { return false; } let isValidLocation = false; for (const location of definition.locations) { switch (location.value) { case `INPUT_FIELD_DEFINITION`: isValidLocation = node.kind === graphql_1.Kind.INPUT_VALUE_DEFINITION || isValidLocation; break; } } return isValidLocation; } function matchArgumentDirective(definition, directive, node) { if (definition.name.value !== directive.name.value) { return false; } let isValidLocation = false; for (const location of definition.locations) { switch (location.value) { case `ARGUMENT_DEFINITION`: isValidLocation = node.kind === graphql_1.Kind.INPUT_VALUE_DEFINITION || isValidLocation; break; } } return isValidLocation; } function matchEnumValueDirective(definition, directive, node) { if (definition.name.value !== directive.name.value) { return false; } let isValidLocation = false; for (const location of definition.locations) { switch (location.value) { case `ENUM_VALUE`: isValidLocation = node.kind === graphql_1.Kind.ENUM_VALUE_DEFINITION || isValidLocation; break; } } return isValidLocation; } class GraphQLTransform { constructor(options) { this.seenTransformations = {}; if (!options.transformers || options.transformers.length === 0) { throw new Error('Must provide at least one transformer.'); } this.transformers = options.transformers; this.stackMappingOverrides = options.stackMapping || {}; this.transformConfig = options.transformConfig || {}; this.featureFlags = options.featureFlags || new FeatureFlags_1.NoopFeatureFlagProvider(); } transform(schema) { this.seenTransformations = {}; const context = new TransformerContext_1.TransformerContext(schema, this.featureFlags); const validDirectiveNameMap = this.transformers.reduce((acc, t) => ({ ...acc, [t.directive.name.value]: true }), { aws_subscribe: true, aws_auth: true, aws_api_key: true, aws_iam: true, aws_oidc: true, aws_lambda: true, aws_cognito_user_pools: true, deprecated: true, }); let allModelDefinitions = [...context.inputDocument.definitions]; for (const transformer of this.transformers) { allModelDefinitions = allModelDefinitions.concat(...transformer.typeDefinitions, transformer.directive); } const errors = (0, validation_1.validateModelSchema)({ kind: graphql_1.Kind.DOCUMENT, definitions: allModelDefinitions }); if (errors && errors.length) { throw new errors_1.SchemaValidationError(errors); } if (this.transformConfig.ResolverConfig) { this.createResourcesForSyncEnabledProject(context); context.setResolverConfig(this.transformConfig.ResolverConfig); } context.setTransformerVersion(this.transformConfig.Version); for (const transformer of this.transformers) { if (isFunction(transformer.before)) { transformer.before(context); } for (const def of context.inputDocument.definitions) { switch (def.kind) { case 'ObjectTypeDefinition': this.transformObject(transformer, def, validDirectiveNameMap, context); break; case 'InterfaceTypeDefinition': this.transformInterface(transformer, def, validDirectiveNameMap, context); break; case 'ScalarTypeDefinition': this.transformScalar(transformer, def, validDirectiveNameMap, context); break; case 'UnionTypeDefinition': this.transformUnion(transformer, def, validDirectiveNameMap, context); break; case 'EnumTypeDefinition': this.transformEnum(transformer, def, validDirectiveNameMap, context); break; case 'InputObjectTypeDefinition': this.transformInputObject(transformer, def, validDirectiveNameMap, context); break; default: continue; } } } let reverseThroughTransformers = this.transformers.length - 1; while (reverseThroughTransformers >= 0) { const transformer = this.transformers[reverseThroughTransformers]; if (isFunction(transformer.after)) { transformer.after(context); } reverseThroughTransformers -= 1; } this.updateContextForStackMappingOverrides(context); const formatter = new TransformFormatter_1.TransformFormatter(); return formatter.format(context); } updateContextForStackMappingOverrides(context) { for (const resourceId of Object.keys(this.stackMappingOverrides)) { context.mapResourceToStack(this.stackMappingOverrides[resourceId], resourceId); } } createResourcesForSyncEnabledProject(context) { const syncResources = { [graphql_transformer_common_1.SyncResourceIDs.syncDataSourceID]: util_1.SyncUtils.createSyncTable(), }; context.mergeResources(syncResources); } transformObject(transformer, def, validDirectiveNameMap, context) { let index = 0; for (const dir of def.directives) { if (!validDirectiveNameMap[dir.name.value]) { throw new errors_1.UnknownDirectiveError(`Unknown directive '${dir.name.value}'. Either remove the directive from the schema or add a transformer to handle it.`); } if (matchDirective(transformer.directive, dir, def)) { if (isFunction(transformer.object)) { const transformKey = makeSeenTransformationKey(dir, def, undefined, undefined, index); if (!this.seenTransformations[transformKey]) { transformer.object(def, dir, context); this.seenTransformations[transformKey] = true; } } else { throw new errors_1.InvalidTransformerError(`The transformer '${transformer.name}' must implement the 'object()' method`); } } index++; } for (const field of def.fields) { this.transformField(transformer, def, field, validDirectiveNameMap, context); } } transformField(transformer, parent, def, validDirectiveNameMap, context) { let index = 0; for (const dir of def.directives) { if (!validDirectiveNameMap[dir.name.value]) { throw new errors_1.UnknownDirectiveError(`Unknown directive '${dir.name.value}'. Either remove the directive from the schema or add a transformer to handle it.`); } if (matchFieldDirective(transformer.directive, dir, def)) { if (isFunction(transformer.field)) { const transformKey = makeSeenTransformationKey(dir, parent, def, undefined, index); if (!this.seenTransformations[transformKey]) { transformer.field(parent, def, dir, context); this.seenTransformations[transformKey] = true; } } else { throw new errors_1.InvalidTransformerError(`The transformer '${transformer.name}' must implement the 'field()' method`); } } index++; } for (const arg of def.arguments) { this.transformArgument(transformer, parent, def, arg, validDirectiveNameMap, context); } } transformArgument(transformer, parent, field, arg, validDirectiveNameMap, context) { let index = 0; for (const dir of arg.directives) { if (!validDirectiveNameMap[dir.name.value]) { throw new errors_1.UnknownDirectiveError(`Unknown directive '${dir.name.value}'. Either remove the directive from the schema or add a transformer to handle it.`); } if (matchArgumentDirective(transformer.directive, dir, arg)) { if (isFunction(transformer.argument)) { const transformKey = makeSeenTransformationKey(dir, parent, field, arg, index); if (!this.seenTransformations[transformKey]) { transformer.argument(arg, dir, context); this.seenTransformations[transformKey] = true; } } else { throw new errors_1.InvalidTransformerError(`The transformer '${transformer.name}' must implement the 'argument()' method`); } } index++; } } transformInterface(transformer, def, validDirectiveNameMap, context) { let index = 0; for (const dir of def.directives) { if (!validDirectiveNameMap[dir.name.value]) { throw new errors_1.UnknownDirectiveError(`Unknown directive '${dir.name.value}'. Either remove the directive from the schema or add a transformer to handle it.`); } if (matchDirective(transformer.directive, dir, def)) { if (isFunction(transformer.interface)) { const transformKey = makeSeenTransformationKey(dir, def, undefined, undefined, index); if (!this.seenTransformations[transformKey]) { transformer.interface(def, dir, context); this.seenTransformations[transformKey] = true; } } else { throw new errors_1.InvalidTransformerError(`The transformer '${transformer.name}' must implement the 'interface()' method`); } } index++; } for (const field of def.fields) { this.transformField(transformer, def, field, validDirectiveNameMap, context); } } transformScalar(transformer, def, validDirectiveNameMap, context) { let index = 0; for (const dir of def.directives) { if (!validDirectiveNameMap[dir.name.value]) { throw new errors_1.UnknownDirectiveError(`Unknown directive '${dir.name.value}'. Either remove the directive from the schema or add a transformer to handle it.`); } if (matchDirective(transformer.directive, dir, def)) { if (isFunction(transformer.scalar)) { const transformKey = makeSeenTransformationKey(dir, def, undefined, undefined, index); if (!this.seenTransformations[transformKey]) { transformer.scalar(def, dir, context); this.seenTransformations[transformKey] = true; } } else { throw new errors_1.InvalidTransformerError(`The transformer '${transformer.name}' must implement the 'scalar()' method`); } } index++; } } transformUnion(transformer, def, validDirectiveNameMap, context) { let index = 0; for (const dir of def.directives) { if (!validDirectiveNameMap[dir.name.value]) { throw new errors_1.UnknownDirectiveError(`Unknown directive '${dir.name.value}'. Either remove the directive from the schema or add a transformer to handle it.`); } if (matchDirective(transformer.directive, dir, def)) { if (isFunction(transformer.union)) { const transformKey = makeSeenTransformationKey(dir, def, undefined, undefined, index); if (!this.seenTransformations[transformKey]) { transformer.union(def, dir, context); this.seenTransformations[transformKey] = true; } } else { throw new errors_1.InvalidTransformerError(`The transformer '${transformer.name}' must implement the 'union()' method`); } } index++; } } transformEnum(transformer, def, validDirectiveNameMap, context) { let index = 0; for (const dir of def.directives) { if (!validDirectiveNameMap[dir.name.value]) { throw new errors_1.UnknownDirectiveError(`Unknown directive '${dir.name.value}'. Either remove the directive from the schema or add a transformer to handle it.`); } if (matchDirective(transformer.directive, dir, def)) { if (isFunction(transformer.enum)) { const transformKey = makeSeenTransformationKey(dir, def, undefined, undefined, index); if (!this.seenTransformations[transformKey]) { transformer.enum(def, dir, context); this.seenTransformations[transformKey] = true; } } else { throw new errors_1.InvalidTransformerError(`The transformer '${transformer.name}' must implement the 'enum()' method`); } } index++; } for (const value of def.values) { this.transformEnumValue(transformer, def, value, validDirectiveNameMap, context); } } transformEnumValue(transformer, enm, def, validDirectiveNameMap, context) { let index = 0; for (const dir of def.directives) { if (!validDirectiveNameMap[dir.name.value]) { throw new errors_1.UnknownDirectiveError(`Unknown directive '${dir.name.value}'. Either remove the directive from the schema or add a transformer to handle it.`); } if (matchEnumValueDirective(transformer.directive, dir, def)) { if (isFunction(transformer.enumValue)) { const transformKey = makeSeenTransformationKey(dir, enm, def, undefined, index); if (!this.seenTransformations[transformKey]) { transformer.enumValue(def, dir, context); this.seenTransformations[transformKey] = true; } } else { throw new errors_1.InvalidTransformerError(`The transformer '${transformer.name}' must implement the 'enumValue()' method`); } } index++; } } transformInputObject(transformer, def, validDirectiveNameMap, context) { let index = 0; for (const dir of def.directives) { if (!validDirectiveNameMap[dir.name.value]) { throw new errors_1.UnknownDirectiveError(`Unknown directive '${dir.name.value}'. Either remove the directive from the schema or add a transformer to handle it.`); } if (matchDirective(transformer.directive, dir, def)) { if (isFunction(transformer.input)) { const transformKey = makeSeenTransformationKey(dir, def, undefined, undefined, index); if (!this.seenTransformations[transformKey]) { transformer.input(def, dir, context); this.seenTransformations[transformKey] = true; } } else { throw new errors_1.InvalidTransformerError(`The transformer '${transformer.name}' must implement the 'input()' method`); } } index++; } for (const field of def.fields) { this.transformInputField(transformer, def, field, validDirectiveNameMap, context); } } transformInputField(transformer, input, def, validDirectiveNameMap, context) { let index = 0; for (const dir of def.directives) { if (!validDirectiveNameMap[dir.name.value]) { throw new errors_1.UnknownDirectiveError(`Unknown directive '${dir.name.value}'. Either remove the directive from the schema or add a transformer to handle it.`); } if (matchInputFieldDirective(transformer.directive, dir, def)) { if (isFunction(transformer.inputValue)) { const transformKey = makeSeenTransformationKey(dir, input, def, undefined, index); if (!this.seenTransformations[transformKey]) { transformer.inputValue(def, dir, context); this.seenTransformations[transformKey] = true; } } else { throw new errors_1.InvalidTransformerError(`The transformer '${transformer.name}' must implement the 'inputValue()' method`); } } index++; } } } exports.GraphQLTransform = GraphQLTransform; //# sourceMappingURL=GraphQLTransform.js.map