UNPKG

graphql-transformer-core

Version:

A framework to transform from GraphQL SDL to AWS cloudFormation.

220 lines • 11.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const cloudform_types_1 = require("cloudform-types"); const getTemplateReferences_1 = require("./getTemplateReferences"); const getIn_1 = __importDefault(require("./getIn")); const setIn_1 = __importDefault(require("./setIn")); const blankTemplate_1 = __importDefault(require("./blankTemplate")); function splitStack(opts) { const stack = opts.stack; const stackRules = opts.stackRules; const rootStackName = opts.rootStackName || 'root'; const defaultParameterValues = opts.defaultParameterValues || {}; const defaultParameterDefinitions = opts.defaultParameterDefinitions || {}; const defaultDependencies = opts.defaultDependencies || []; const importExportPrefix = opts.importExportPrefix; function createMapByStackRules(keys) { const stackMap = {}; for (const key of keys) { const mappedTo = stackRules.get(key); if (mappedTo) { stackMap[key] = mappedTo; } else { stackMap[key] = rootStackName; } } return stackMap; } function mapResourcesToStack(template) { return createMapByStackRules(Object.keys(template.Resources)); } function mapMappingToStack(template) { return createMapByStackRules(Object.keys(template.Mappings)); } function mapOutputsToStack(template) { return createMapByStackRules(Object.keys(template.Outputs)); } function collectTemplates(template, resourceToStackMap, outputToStackMap, mappingsToStackMap) { const resourceIds = Object.keys(resourceToStackMap); const templateMap = {}; for (const resourceId of resourceIds) { const stackName = resourceToStackMap[resourceId]; if (!templateMap[stackName]) { templateMap[stackName] = (0, blankTemplate_1.default)({ Description: 'An auto-generated nested stack.', Parameters: { ...template.Parameters, ...defaultParameterDefinitions, }, Conditions: template.Conditions, }); } const resource = template.Resources[resourceId]; let depends = resource.DependsOn; if (depends && Array.isArray(depends)) { resource.DependsOn = depends.filter((id) => { return resourceToStackMap[id] === stackName; }); } else if (depends && typeof depends === 'string') { resource.DependsOn = resourceToStackMap[depends] === stackName ? depends : undefined; } templateMap[stackName].Resources[resourceId] = resource; } const outputIds = Object.keys(outputToStackMap); for (const outputId of outputIds) { const stackName = outputToStackMap[outputId]; const output = template.Outputs[outputId]; templateMap[stackName].Outputs[outputId] = output; } const mappingIds = Object.keys(mappingToStackMap); for (const mappingId of mappingIds) { const stackName = mappingsToStackMap[mappingId]; const mappings = template.Mappings[mappingId]; templateMap[stackName].Mappings[mappingId] = mappings; } templateMap[rootStackName].Parameters = template.Parameters; templateMap[rootStackName].Conditions = template.Conditions; return templateMap; } function replaceReferences(stacks, resourceToStackMap) { const stackDependsOnMap = Object.keys(stacks).reduce((acc, k) => ({ ...acc, [k]: [] }), {}); const stackParamsMap = Object.keys(stacks).reduce((acc, k) => ({ ...acc, [k]: {} }), {}); for (const thisStackName of Object.keys(stacks)) { const template = stacks[thisStackName]; const resourceToReferenceMap = (0, getTemplateReferences_1.getTemplateReferences)(template); for (const resourceId of Object.keys(resourceToReferenceMap)) { const references = resourceToReferenceMap[resourceId]; const referencedStackName = resourceToStackMap[resourceId]; for (const refList of references) { const refNode = (0, getIn_1.default)(template, refList); const refNeedsReplacing = refNode && refNode.Ref && referencedStackName && referencedStackName !== thisStackName; const getAttNeedsReplacing = refNode && refNode['Fn::GetAtt'] && referencedStackName && referencedStackName !== thisStackName; const isChildReferencingRoot = thisStackName !== rootStackName && referencedStackName === rootStackName; if (refNeedsReplacing && isChildReferencingRoot) { const parameterName = `Ref${resourceId}`; stackParamsMap[thisStackName][parameterName] = refNode; template.Parameters[parameterName] = new cloudform_types_1.StringParameter({ Description: `Auto-generated parameter that forwards Fn.Ref(${resourceId}) through to nested stacks.`, }); (0, setIn_1.default)(template, refList, cloudform_types_1.Fn.Ref(parameterName)); } else if (refNeedsReplacing) { (0, setIn_1.default)(template, refList, makeImportValueForRef(resourceId)); const outputForInput = makeOutputForRef(resourceId); const referencedStack = stacks[referencedStackName]; const exportLogicalId = `Ref${resourceId}`; if (referencedStack && referencedStack.Outputs && !referencedStack.Outputs[exportLogicalId]) { if (template.Outputs[exportLogicalId]) { delete template.Outputs[exportLogicalId]; } referencedStack.Outputs[exportLogicalId] = outputForInput; } if (stackDependsOnMap[thisStackName] && !stackDependsOnMap[thisStackName].find((s) => s === referencedStackName)) { stackDependsOnMap[thisStackName].push(referencedStackName); } } else if (getAttNeedsReplacing && isChildReferencingRoot) { const [resId, attr] = refNode['Fn::GetAtt']; const parameterName = `GetAtt${resourceId}${attr}`; stackParamsMap[thisStackName][parameterName] = refNode; template.Parameters[parameterName] = new cloudform_types_1.StringParameter({ Description: `Auto-generated parameter that forwards Fn.GetAtt(${resourceId}, ${attr}) through to nested stacks.`, }); (0, setIn_1.default)(template, refList, cloudform_types_1.Fn.Ref(parameterName)); } else if (getAttNeedsReplacing) { const [resId, attr] = refNode['Fn::GetAtt']; (0, setIn_1.default)(template, refList, makeImportValueForGetAtt(resourceId, attr)); const outputForInput = makeOutputForGetAtt(resourceId, attr); const referencedStack = stacks[referencedStackName]; const exportLogicalId = `GetAtt${resourceId}${attr}`; if (referencedStack && referencedStack.Outputs && !referencedStack.Outputs[exportLogicalId]) { if (template.Outputs[exportLogicalId]) { delete template.Outputs[exportLogicalId]; } referencedStack.Outputs[exportLogicalId] = outputForInput; } if (stackDependsOnMap[thisStackName] && !stackDependsOnMap[thisStackName].find((s) => s === referencedStackName)) { stackDependsOnMap[thisStackName].push(referencedStackName); } } } } } return { stackDependencyMap: stackDependsOnMap, stackParameterMap: stackParamsMap, }; } function makeImportValueForRef(resourceId) { return cloudform_types_1.Fn.ImportValue(cloudform_types_1.Fn.Join(':', [importExportPrefix, 'Ref', resourceId])); } function makeImportValueForGetAtt(resourceId, attribute) { return cloudform_types_1.Fn.ImportValue(cloudform_types_1.Fn.Join(':', [importExportPrefix, 'GetAtt', resourceId, attribute])); } function makeOutputForGetAtt(resourceId, attribute) { return { Value: cloudform_types_1.Fn.GetAtt(resourceId, attribute), Export: { Name: cloudform_types_1.Fn.Join(':', [importExportPrefix, 'GetAtt', resourceId, attribute]), }, }; } function makeOutputForRef(resourceId) { return { Value: cloudform_types_1.Fn.Ref(resourceId), Export: { Name: cloudform_types_1.Fn.Join(':', [importExportPrefix, 'Ref', resourceId]), }, }; } function updateRootWithNestedStacks(root, stacks, stackInfo) { const stackFileNames = Object.keys(stacks); const allParamNames = Object.keys(root.Parameters); const allParamValues = allParamNames.reduce((acc, name) => ({ ...acc, [name]: cloudform_types_1.Fn.Ref(name), }), defaultParameterValues); for (const stackName of stackFileNames) { const dependsOnStacks = stackInfo.stackDependencyMap[stackName] || []; const extraParams = stackInfo.stackParameterMap[stackName] || {}; let stackResource = new cloudform_types_1.CloudFormation.Stack({ Parameters: { ...allParamValues, ...extraParams, }, TemplateURL: cloudform_types_1.Fn.Join('/', [ 'https://s3.amazonaws.com', cloudform_types_1.Fn.Ref(opts.deployment.deploymentBucketParameterName), cloudform_types_1.Fn.Ref(opts.deployment.deploymentKeyParameterName), 'stacks', stackName + '.json', ]), }).dependsOn([...defaultDependencies, ...dependsOnStacks]); root.Resources[stackName] = stackResource; } return root; } const templateJson = JSON.parse(JSON.stringify(stack)); const resourceToStackMap = mapResourcesToStack(templateJson); const outputToStackMap = mapOutputsToStack(templateJson); const mappingToStackMap = mapMappingToStack(templateJson); const stackMapping = { ...resourceToStackMap, ...outputToStackMap, ...mappingToStackMap }; const stacks = collectTemplates(templateJson, resourceToStackMap, outputToStackMap, stackMapping); const stackInfo = replaceReferences(stacks, resourceToStackMap); let rootStack = stacks[rootStackName]; delete stacks[rootStackName]; rootStack = updateRootWithNestedStacks(rootStack, stacks, stackInfo); return { rootStack, stacks, stackMapping, }; } exports.default = splitStack; //# sourceMappingURL=splitStack.js.map