graphql-transformer-core
Version:
A framework to transform from GraphQL SDL to AWS cloudFormation.
450 lines • 21.3 kB
JavaScript
"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