graphql-transformer-core
Version:
A framework to transform from GraphQL SDL to AWS cloudFormation.
528 lines • 20.2 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TransformerContext = exports.TransformerContextMetadata = exports.objectExtension = exports.blankObject = void 0;
const graphql_1 = require("graphql");
const graphql_transformer_common_1 = require("graphql-transformer-common");
const blankTemplate_1 = __importDefault(require("./util/blankTemplate"));
const defaultSchema_1 = __importDefault(require("./defaultSchema"));
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 TransformerContextMetadata {
constructor() {
this.metadata = {};
}
get(key) {
return this.metadata[key];
}
set(key, val) {
return (this.metadata[key] = val);
}
has(key) {
return Boolean(this.metadata[key] !== undefined);
}
}
exports.TransformerContextMetadata = TransformerContextMetadata;
class TransformerContext {
constructor(inputSDL, featureFlags) {
this.featureFlags = featureFlags;
this.template = (0, blankTemplate_1.default)();
this.nodeMap = {};
this.metadata = new TransformerContextMetadata();
this.stackMapping = new Map();
const isInputSDLEmpty = inputSDL.trim().length === 0;
if (isInputSDLEmpty) {
this.inputDocument = {
kind: graphql_1.Kind.DOCUMENT,
definitions: [],
};
}
else {
const doc = (0, graphql_1.parse)(inputSDL);
for (const def of doc.definitions) {
if (def.kind === 'OperationDefinition' || def.kind === 'FragmentDefinition') {
throw new Error(`Found a ${def.kind}. Transformers accept only documents consisting of TypeSystemDefinitions.`);
}
}
this.inputDocument = doc;
}
this.fillNodeMapWithInput();
}
fillNodeMapWithInput() {
const extensionNodes = [];
for (const inputDef of this.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.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;
case graphql_1.Kind.SCALAR_TYPE_EXTENSION:
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;
case graphql_1.Kind.SCALAR_TYPE_EXTENSION:
default:
continue;
}
}
if (!this.getSchema()) {
this.putSchema(defaultSchema_1.default);
}
}
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;
}
mergeResources(resources) {
for (const resourceId of Object.keys(resources)) {
if (this.template.Resources[resourceId]) {
throw new Error(`Conflicting CloudFormation resource logical id: ${resourceId}`);
}
}
this.template.Resources = { ...this.template.Resources, ...resources };
}
mergeParameters(params) {
for (const parameterName of Object.keys(params)) {
if (this.template.Parameters[parameterName]) {
throw new Error(`Conflicting CloudFormation parameter name: ${parameterName}`);
}
}
this.template.Parameters = { ...this.template.Parameters, ...params };
}
mergeConditions(conditions) {
if (!this.template.Conditions) {
this.template.Conditions = {};
}
for (const conditionName of Object.keys(conditions)) {
if (this.template.Conditions[conditionName]) {
throw new Error(`Conflicting CloudFormation condition name: ${conditionName}`);
}
}
this.template.Conditions = { ...this.template.Conditions, ...conditions };
}
getResource(resource) {
return this.template.Resources[resource];
}
setResource(key, resource) {
this.template.Resources[key] = resource;
}
setOutput(key, output) {
this.template.Outputs[key] = output;
}
getOutput(key) {
return this.template.Outputs[key];
}
mergeOutputs(outputs) {
for (const outputName of Object.keys(outputs)) {
if (this.template.Parameters[outputName]) {
throw new Error(`Conflicting CloudFormation parameter name: ${outputName}`);
}
}
this.template.Outputs = { ...this.template.Outputs, ...outputs };
}
mergeMappings(mapping) {
for (const mappingName of Object.keys(mapping)) {
if (this.template.Mappings[mappingName]) {
throw new Error(`Conflicting CloudFormation mapping name: ${mappingName}`);
}
}
this.template.Mappings = { ...this.template.Mappings, ...mapping };
}
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];
}
}
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];
}
}
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];
}
}
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];
}
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;
}
}
}
addQueryFields(fields) {
const queryTypeName = this.getQueryTypeName();
if (queryTypeName) {
if (!this.getType(queryTypeName)) {
this.addType(blankObject(queryTypeName));
}
let queryType = objectExtension(queryTypeName, fields);
this.addObjectExtension(queryType);
}
}
addMutationFields(fields) {
let mutationTypeName = this.getMutationTypeName();
if (!mutationTypeName) {
const mutationNode = this.getMutation();
if (!mutationNode) {
const schema = this.getSchema();
const mutationOperation = (0, graphql_transformer_common_1.makeOperationType)('mutation', 'Mutation');
const newSchema = {
...schema,
operationTypes: [...schema.operationTypes, mutationOperation],
};
this.putSchema(newSchema);
mutationTypeName = 'Mutation';
}
}
if (mutationTypeName) {
if (!this.getType(mutationTypeName)) {
this.addType(blankObject(mutationTypeName));
}
let mutationType = objectExtension(mutationTypeName, fields);
this.addObjectExtension(mutationType);
}
}
addSubscriptionFields(fields) {
let subscriptionTypeName = this.getSubscriptionTypeName();
if (!subscriptionTypeName) {
const subscriptionNode = this.getSubscription();
if (!subscriptionNode) {
const schema = this.getSchema();
const subscriptionOperation = (0, graphql_transformer_common_1.makeOperationType)('subscription', 'Subscription');
const newSchema = {
...schema,
operationTypes: [...schema.operationTypes, subscriptionOperation],
};
this.putSchema(newSchema);
subscriptionTypeName = 'Subscription';
}
}
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);
const newDirs = [];
const oldDirs = 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.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.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;
}
addEnum(en) {
if (this.nodeMap[en.name.value]) {
throw new Error(`Conflicting enum type '${en.name.value}' found.`);
}
this.nodeMap[en.name.value] = en;
}
mapResourceToStack(stackName, resource) {
this.stackMapping.set(resource, stackName);
}
getStackMapping() {
return this.stackMapping;
}
setResolverConfig(resolverConfig) {
if (this.resolverConfig) {
throw new Error(`Resolver Configuration has already been added to the context`);
}
this.resolverConfig = resolverConfig;
}
getResolverConfig() {
return this.resolverConfig;
}
setTransformerVersion(version) {
this.transformerVersion = version;
}
getTransformerVersion() {
return this.transformerVersion;
}
isProjectUsingDataStore() {
return this.resolverConfig && (typeof this.resolverConfig.project !== undefined || typeof this.resolverConfig.models !== undefined);
}
}
exports.TransformerContext = TransformerContext;
//# sourceMappingURL=TransformerContext.js.map