UNPKG

@aws-amplify/cli-internal

Version:
347 lines • 26.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RestApiRenderer = void 0; const typescript_1 = __importDefault(require("typescript")); const ts_1 = require("../../ts"); const factory = typescript_1.default.factory; class RestApiRenderer { constructor(hasAuth) { this.hasAuth = hasAuth; } render(restApi) { return factory.createNodeArray([ ...this.renderImports(), this.renderBackendTypeImport(), ts_1.newLineIdentifier, ts_1.TS.createBranchNameDeclaration(), ts_1.newLineIdentifier, this.renderDefineApi(restApi), ]); } renderImports() { return [ ts_1.TS.namedImport('aws-cdk-lib/aws-apigateway', 'RestApi', 'LambdaIntegration', 'AuthorizationType', 'Cors', 'ResponseType'), ts_1.TS.namedImport('aws-cdk-lib/aws-iam', 'Policy', 'PolicyStatement'), ts_1.TS.namedImport('aws-cdk-lib', 'Stack'), ]; } renderBackendTypeImport() { return ts_1.TS.typeImport('../../backend', 'Backend'); } renderDefineApi(restApi) { const statements = this.renderApi(restApi); return ts_1.TS.exportedFunction(restApi.exportedFunctionName, statements); } renderApi(restApi) { const statements = []; const sanitizedName = restApi.apiName.replace(/[^a-zA-Z0-9]/g, ''); const apiVarName = `${sanitizedName}Api`; const gen1ApiVarName = `gen1${sanitizedName}Api`; const gen1PolicyVarName = `gen1${sanitizedName}Policy`; statements.push(this.renderStack(restApi, 'stack')); statements.push(this.renderRestApiConstruct(restApi, 'stack', apiVarName)); statements.push(...this.renderGatewayResponses(apiVarName)); const integrations = this.renderLambdaIntegrations(restApi); statements.push(...integrations.statements); statements.push(this.renderGen1ApiReference(restApi, 'stack', gen1ApiVarName)); statements.push(this.renderGen1Policy(restApi, 'stack', gen1ApiVarName, gen1PolicyVarName)); if (this.hasPathAuth(restApi) && this.hasAuth) { statements.push(this.renderGen1PolicyAttachment(gen1PolicyVarName)); } statements.push(...this.renderPaths(restApi, apiVarName, integrations.map)); statements.push(...this.renderPathPolicies(restApi, apiVarName, 'stack', gen1ApiVarName)); statements.push(this.renderOutput(apiVarName)); return statements; } extractMethods(pathConfig) { var _a, _b, _c; if (((_b = (_a = pathConfig.permissions) === null || _a === void 0 ? void 0 : _a.auth) === null || _b === void 0 ? void 0 : _b.length) > 0) { return this.mapPermissionsToMethods(pathConfig.permissions.auth); } if ((_c = pathConfig.permissions) === null || _c === void 0 ? void 0 : _c.groups) { const allPermissions = new Set(); for (const permissions of Object.values(pathConfig.permissions.groups)) { for (const permission of permissions) { allPermissions.add(permission); } } return this.mapPermissionsToMethods(Array.from(allPermissions)); } return ['GET']; } mapPermissionsToMethods(permissions) { const methodMap = { read: 'GET', create: 'POST', update: 'PUT', delete: 'DELETE', }; const methods = permissions.map((p) => methodMap[p]).filter((m) => m !== undefined); return methods.length > 0 ? methods : ['GET']; } renderStack(restApi, stackVarName) { return factory.createVariableStatement([], factory.createVariableDeclarationList([ factory.createVariableDeclaration(stackVarName, undefined, undefined, factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier('backend'), factory.createIdentifier('createStack')), undefined, [factory.createStringLiteral(`rest-api-stack-${restApi.apiName}`)])), ], typescript_1.default.NodeFlags.Const)); } renderRestApiConstruct(restApi, stackVarName, apiVarName) { return factory.createVariableStatement([], factory.createVariableDeclarationList([ factory.createVariableDeclaration(apiVarName, undefined, undefined, factory.createNewExpression(factory.createIdentifier('RestApi'), undefined, [ factory.createIdentifier(stackVarName), factory.createStringLiteral('RestApi'), factory.createObjectLiteralExpression([ factory.createPropertyAssignment('restApiName', factory.createTemplateExpression(factory.createTemplateHead(`${restApi.apiName}-`), [ factory.createTemplateSpan(factory.createIdentifier('branchName'), factory.createTemplateTail('')), ])), ], true), ])), ], typescript_1.default.NodeFlags.Const)); } renderGatewayResponses(apiVarName) { return [ this.renderGatewayResponse(apiVarName, 'Default4XX', 'DEFAULT_4XX'), this.renderGatewayResponse(apiVarName, 'Default5XX', 'DEFAULT_5XX'), ]; } renderGatewayResponse(apiVarName, name, responseType) { return factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier(apiVarName), factory.createIdentifier('addGatewayResponse')), undefined, [ factory.createStringLiteral(name), factory.createObjectLiteralExpression([ ts_1.TS.enumProp('type', 'ResponseType', responseType), factory.createPropertyAssignment('responseHeaders', factory.createObjectLiteralExpression([ factory.createPropertyAssignment(factory.createStringLiteral('Access-Control-Allow-Origin'), factory.createStringLiteral("'*'")), factory.createPropertyAssignment(factory.createStringLiteral('Access-Control-Allow-Headers'), factory.createStringLiteral("'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'")), factory.createPropertyAssignment(factory.createStringLiteral('Access-Control-Allow-Methods'), factory.createStringLiteral("'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'")), factory.createPropertyAssignment(factory.createStringLiteral('Access-Control-Expose-Headers'), factory.createStringLiteral("'Date,X-Amzn-ErrorType'")), ], true)), ], true), ])); } renderLambdaIntegrations(restApi) { const statements = []; const map = new Map(); const uniqueFunctions = new Set(); for (const pathConfig of Object.values(restApi.paths)) { uniqueFunctions.add(pathConfig.lambdaFunction); } for (const funcName of uniqueFunctions) { const integrationVarName = `${funcName}Integration`; map.set(funcName, integrationVarName); statements.push(factory.createVariableStatement([], factory.createVariableDeclarationList([ factory.createVariableDeclaration(integrationVarName, undefined, undefined, factory.createNewExpression(factory.createIdentifier('LambdaIntegration'), undefined, [ factory.createPropertyAccessExpression(factory.createPropertyAccessExpression(factory.createPropertyAccessExpression(factory.createIdentifier('backend'), factory.createIdentifier(funcName)), factory.createIdentifier('resources')), factory.createIdentifier('lambda')), ])), ], typescript_1.default.NodeFlags.Const))); } return { statements, map }; } renderGen1ApiReference(restApi, stackVarName, gen1ApiVarName) { return factory.createVariableStatement([], factory.createVariableDeclarationList([ factory.createVariableDeclaration(gen1ApiVarName, undefined, undefined, factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier('RestApi'), factory.createIdentifier('fromRestApiAttributes')), undefined, [ factory.createIdentifier(stackVarName), factory.createStringLiteral(`Gen1${restApi.apiName}Api`), factory.createObjectLiteralExpression([ factory.createPropertyAssignment('restApiId', factory.createStringLiteral(restApi.gen1ApiId)), factory.createPropertyAssignment('rootResourceId', factory.createStringLiteral(restApi.gen1RootResourceId)), ], true), ])), ], typescript_1.default.NodeFlags.Const)); } renderGen1Policy(restApi, stackVarName, gen1ApiVarName, gen1PolicyVarName) { return factory.createVariableStatement([], factory.createVariableDeclarationList([ factory.createVariableDeclaration(gen1PolicyVarName, undefined, undefined, factory.createNewExpression(factory.createIdentifier('Policy'), undefined, [ factory.createIdentifier(stackVarName), factory.createStringLiteral(`Gen1${restApi.apiName}Policy`), factory.createObjectLiteralExpression([ factory.createPropertyAssignment('statements', factory.createArrayLiteralExpression([ factory.createNewExpression(factory.createIdentifier('PolicyStatement'), undefined, [ factory.createObjectLiteralExpression([ factory.createPropertyAssignment('actions', factory.createArrayLiteralExpression([factory.createStringLiteral('execute-api:Invoke')])), factory.createPropertyAssignment('resources', factory.createArrayLiteralExpression([ ...Object.entries(restApi.paths).flatMap(([, pathConfig]) => this.extractMethods(pathConfig).map((method) => factory.createTemplateExpression(factory.createTemplateHead(''), [ factory.createTemplateSpan(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier(gen1ApiVarName), factory.createIdentifier('arnForExecuteApi')), undefined, [factory.createStringLiteral(method), factory.createStringLiteral('/*')]), factory.createTemplateTail('')), ]))), ])), ], true), ]), ])), ], true), ])), ], typescript_1.default.NodeFlags.Const)); } renderGen1PolicyAttachment(gen1PolicyVarName) { return factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createPropertyAccessExpression(factory.createIdentifier('backend.auth.resources'), factory.createIdentifier('authenticatedUserIamRole')), factory.createIdentifier('attachInlinePolicy')), undefined, [factory.createIdentifier(gen1PolicyVarName)])); } renderPaths(restApi, apiVarName, integrations) { var _a, _b; const statements = []; for (const [pathName, pathConfig] of Object.entries(restApi.paths)) { const pathSegments = pathName.split('/').filter((segment) => segment && segment !== '{proxy+}'); const resourceName = pathSegments.join('').replace(/[^a-zA-Z0-9]/g, '') || 'root'; let resourceExpression = factory.createPropertyAccessExpression(factory.createIdentifier(apiVarName), factory.createIdentifier('root')); for (let i = 0; i < pathSegments.length; i++) { const isLastSegment = i === pathSegments.length - 1; const resourceArgs = [factory.createStringLiteral(pathSegments[i])]; if (isLastSegment) { const resourceOptions = []; if (((_a = pathConfig.permissions) === null || _a === void 0 ? void 0 : _a.setting) === 'private') { resourceOptions.push(factory.createPropertyAssignment('defaultMethodOptions', factory.createObjectLiteralExpression([ts_1.TS.enumProp('authorizationType', 'AuthorizationType', 'IAM')], true))); } resourceOptions.push(this.renderCorsPreflightOptions()); resourceArgs.push(factory.createObjectLiteralExpression(resourceOptions, true)); } resourceExpression = factory.createCallExpression(factory.createPropertyAccessExpression(resourceExpression, factory.createIdentifier('addResource')), undefined, resourceArgs); } statements.push(factory.createVariableStatement([], factory.createVariableDeclarationList([factory.createVariableDeclaration(resourceName, undefined, undefined, resourceExpression)], typescript_1.default.NodeFlags.Const))); const integrationVar = (_b = integrations.get(pathConfig.lambdaFunction)) !== null && _b !== void 0 ? _b : `${pathConfig.lambdaFunction}Integration`; statements.push(factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier(resourceName), factory.createIdentifier('addMethod')), undefined, [factory.createStringLiteral('ANY'), factory.createIdentifier(integrationVar)]))); statements.push(factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier(resourceName), factory.createIdentifier('addProxy')), undefined, [ factory.createObjectLiteralExpression([ factory.createPropertyAssignment('anyMethod', factory.createTrue()), factory.createPropertyAssignment('defaultIntegration', factory.createIdentifier(integrationVar)), ], true), ]))); } return statements; } renderCorsPreflightOptions() { return factory.createPropertyAssignment('defaultCorsPreflightOptions', factory.createObjectLiteralExpression([ ts_1.TS.enumProp('allowOrigins', 'Cors', 'ALL_ORIGINS'), ts_1.TS.enumProp('allowMethods', 'Cors', 'ALL_METHODS'), factory.createPropertyAssignment('allowHeaders', factory.createArrayLiteralExpression([ factory.createStringLiteral('Content-Type'), factory.createStringLiteral('X-Amz-Date'), factory.createStringLiteral('Authorization'), factory.createStringLiteral('X-Api-Key'), factory.createStringLiteral('X-Amz-Security-Token'), factory.createStringLiteral('X-Amz-User-Agent'), ])), factory.createPropertyAssignment('statusCode', factory.createNumericLiteral('200')), ], true)); } renderPathPolicies(restApi, apiVarName, stackVarName, gen1ApiVarName) { var _a, _b, _c; const statements = []; for (const [pathName, pathConfig] of Object.entries(restApi.paths)) { if (((_b = (_a = pathConfig.permissions) === null || _a === void 0 ? void 0 : _a.auth) === null || _b === void 0 ? void 0 : _b.length) > 0) { statements.push(...this.renderAuthPathPolicy(pathName, pathConfig, apiVarName, stackVarName)); } if ((_c = pathConfig.permissions) === null || _c === void 0 ? void 0 : _c.groups) { for (const groupName of Object.keys(pathConfig.permissions.groups)) { statements.push(...this.renderGroupPathPolicy(pathName, pathConfig, apiVarName, stackVarName, groupName, gen1ApiVarName)); } } } return statements; } renderAuthPathPolicy(pathName, pathConfig, apiVarName, stackVarName) { const comment = factory.createNotEmittedStatement(factory.createStringLiteral('')); typescript_1.default.addSyntheticLeadingComment(comment, typescript_1.default.SyntaxKind.SingleLineCommentTrivia, ` ${pathName} - all authenticated users`, true); const policyName = `${pathName.replace(/[^a-zA-Z0-9]/g, '')}AuthPolicy`; const attachCall = factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createPropertyAccessExpression(factory.createIdentifier('backend.auth.resources'), factory.createIdentifier('authenticatedUserIamRole')), factory.createIdentifier('attachInlinePolicy')), undefined, [ factory.createNewExpression(factory.createIdentifier('Policy'), undefined, [ factory.createIdentifier(stackVarName), factory.createStringLiteral(policyName), factory.createObjectLiteralExpression([ factory.createPropertyAssignment('statements', factory.createArrayLiteralExpression([ factory.createNewExpression(factory.createIdentifier('PolicyStatement'), undefined, [ factory.createObjectLiteralExpression([ factory.createPropertyAssignment('actions', factory.createArrayLiteralExpression([factory.createStringLiteral('execute-api:Invoke')])), factory.createPropertyAssignment('resources', factory.createArrayLiteralExpression([ ...this.extractMethods(pathConfig).flatMap((method) => [ factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier(apiVarName), factory.createIdentifier('arnForExecuteApi')), undefined, [factory.createStringLiteral(method), factory.createStringLiteral(pathName)]), factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier(apiVarName), factory.createIdentifier('arnForExecuteApi')), undefined, [factory.createStringLiteral(method), factory.createStringLiteral(`${pathName}/*`)]), ]), ])), ], true), ]), ])), ], true), ]), ])); return [comment, attachCall]; } renderGroupPathPolicy(pathName, pathConfig, apiVarName, stackVarName, groupName, gen1ApiVarName) { const comment = factory.createNotEmittedStatement(factory.createStringLiteral('')); typescript_1.default.addSyntheticLeadingComment(comment, typescript_1.default.SyntaxKind.SingleLineCommentTrivia, ` ${pathName} - ${groupName} group only`, true); const policyName = `${pathName.replace(/[^a-zA-Z0-9]/g, '')}${groupName}Policy`; const attachCall = factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createPropertyAccessExpression(factory.createElementAccessExpression(factory.createPropertyAccessExpression(factory.createIdentifier('backend.auth.resources'), factory.createIdentifier('groups')), factory.createStringLiteral(groupName)), factory.createIdentifier('role')), factory.createIdentifier('attachInlinePolicy')), undefined, [ factory.createNewExpression(factory.createIdentifier('Policy'), undefined, [ factory.createIdentifier(stackVarName), factory.createStringLiteral(policyName), factory.createObjectLiteralExpression([ factory.createPropertyAssignment('statements', factory.createArrayLiteralExpression([ factory.createNewExpression(factory.createIdentifier('PolicyStatement'), undefined, [ factory.createObjectLiteralExpression([ factory.createPropertyAssignment('actions', factory.createArrayLiteralExpression([factory.createStringLiteral('execute-api:Invoke')])), factory.createPropertyAssignment('resources', factory.createArrayLiteralExpression([ ...this.extractMethods(pathConfig).flatMap((method) => [ factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier(apiVarName), factory.createIdentifier('arnForExecuteApi')), undefined, [factory.createStringLiteral(method), factory.createStringLiteral(pathName)]), factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier(apiVarName), factory.createIdentifier('arnForExecuteApi')), undefined, [factory.createStringLiteral(method), factory.createStringLiteral(`${pathName}/*`)]), ]), ])), ], true), ]), ])), ], true), ]), ])); const pathPart = pathName.replace(/[^a-zA-Z0-9]/g, ''); const capitalizedPathPart = pathPart.charAt(0).toUpperCase() + pathPart.slice(1); const gen1PolicyName = `gen1${capitalizedPathPart}${groupName}Policy`; return [ comment, attachCall, this.renderGen1GroupPathPolicy(pathName, pathConfig, gen1ApiVarName, stackVarName, groupName, gen1PolicyName), ]; } renderGen1GroupPathPolicy(pathName, pathConfig, gen1ApiVarName, stackVarName, groupName, policyName) { return factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createPropertyAccessExpression(factory.createElementAccessExpression(factory.createPropertyAccessExpression(factory.createIdentifier('backend.auth.resources'), factory.createIdentifier('groups')), factory.createStringLiteral(groupName)), factory.createIdentifier('role')), factory.createIdentifier('attachInlinePolicy')), undefined, [ factory.createNewExpression(factory.createIdentifier('Policy'), undefined, [ factory.createIdentifier(stackVarName), factory.createStringLiteral(policyName), factory.createObjectLiteralExpression([ factory.createPropertyAssignment('statements', factory.createArrayLiteralExpression([ factory.createNewExpression(factory.createIdentifier('PolicyStatement'), undefined, [ factory.createObjectLiteralExpression([ factory.createPropertyAssignment('actions', factory.createArrayLiteralExpression([factory.createStringLiteral('execute-api:Invoke')])), factory.createPropertyAssignment('resources', factory.createArrayLiteralExpression([ ...this.extractMethods(pathConfig).flatMap((method) => [ factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier(gen1ApiVarName), factory.createIdentifier('arnForExecuteApi')), undefined, [factory.createStringLiteral(method), factory.createStringLiteral(pathName)]), factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier(gen1ApiVarName), factory.createIdentifier('arnForExecuteApi')), undefined, [factory.createStringLiteral(method), factory.createStringLiteral(`${pathName}/*`)]), ]), ])), ], true), ]), ])), ], true), ]), ])); } renderOutput(apiVarName) { return factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier('backend'), factory.createIdentifier('addOutput')), undefined, [ factory.createObjectLiteralExpression([ factory.createPropertyAssignment('custom', factory.createObjectLiteralExpression([ factory.createPropertyAssignment('API', factory.createObjectLiteralExpression([ factory.createPropertyAssignment(factory.createComputedPropertyName(factory.createPropertyAccessExpression(factory.createIdentifier(apiVarName), factory.createIdentifier('restApiName'))), factory.createObjectLiteralExpression([ factory.createPropertyAssignment('endpoint', factory.createCallExpression(factory.createPropertyAccessExpression(factory.createPropertyAccessExpression(factory.createIdentifier(apiVarName), factory.createIdentifier('url')), factory.createIdentifier('slice')), undefined, [ factory.createNumericLiteral('0'), factory.createPrefixUnaryExpression(typescript_1.default.SyntaxKind.MinusToken, factory.createNumericLiteral('1')), ])), factory.createPropertyAssignment('region', factory.createPropertyAccessExpression(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier('Stack'), factory.createIdentifier('of')), undefined, [factory.createIdentifier(apiVarName)]), factory.createIdentifier('region'))), factory.createPropertyAssignment('apiName', factory.createPropertyAccessExpression(factory.createIdentifier(apiVarName), factory.createIdentifier('restApiName'))), ], true)), ], true)), ], true)), ], true), ])); } hasPathAuth(restApi) { return Object.values(restApi.paths).some((p) => { var _a, _b; return ((_a = p.permissions) === null || _a === void 0 ? void 0 : _a.setting) === 'private' || ((_b = p.permissions) === null || _b === void 0 ? void 0 : _b.setting) === 'protected'; }); } } exports.RestApiRenderer = RestApiRenderer; //# sourceMappingURL=rest-api.renderer.js.map