UNPKG

7.26 kBPlain TextView Raw
1import { ResourceConstants } from './ResourceConstants';
2import DataSource from 'cloudform-types/types/appSync/dataSource';
3import IAM from 'cloudform-types/types/iam';
4
5import cloudform, { Fn, StringParameter, Refs } from 'cloudform';
6import Template from 'cloudform-types/types/template';
7import { TemplateContext } from './RelationalDBSchemaTransformer';
8import { RelationalDBResolverGenerator } from './RelationalDBResolverGenerator';
9
10/**
11 * This is the Class responsible for generating and managing the CloudForm template
12 * provided a TemplateContext object, which is generated by the RelationalDBSchemaTransformer.
13 *
14 * It will generate the basic CloudForm template needed for getting the AppSync API and
15 * RDS DataSource provisioned. It also allows for adding the CRUDL+Q Resolvers upon need.
16 */
17export class RelationalDBTemplateGenerator {
18 context: TemplateContext;
19
20 constructor(context: TemplateContext) {
21 this.context = context;
22 }
23
24 /**
25 * Creates and returns the basic Cloudform template needed for setting
26 * up an AppSync API pointing at the RDS DataSource.
27 *
28 * @returns the created CloudFormation template.
29 */
30 public createTemplate(context: any): Template {
31 const template = {
32 AWSTemplateFormatVersion: '2010-09-09',
33 Parameters: this.makeParameters(this.context.databaseName),
34 Resources: {
35 [ResourceConstants.RESOURCES.RelationalDatabaseDataSource]: this.makeRelationalDataSource(context),
36 [ResourceConstants.RESOURCES.RelationalDatabaseAccessRole]: this.makeIAMDataSourceRole(),
37 },
38 };
39
40 return template;
41 }
42
43 /**
44 * Provided a Cloudform Template, this method adds Resolver Resources to the
45 * Template.
46 *
47 * @param template - the Cloudform template
48 * @returns the given template, updated with new resolvers.
49 */
50 public addRelationalResolvers(template: Template, resolverFilePath: string): Template {
51 let resolverGenerator = new RelationalDBResolverGenerator(this.context);
52 template.Resources = { ...template.Resources, ...resolverGenerator.createRelationalResolvers(resolverFilePath) };
53 return template;
54 }
55
56 /**
57 * Provided a Cloudform Template, this method returns the cfn json template as a string
58 *
59 * @param template - the Cloudform template
60 * @returns the json, string form of the template given.
61 */
62 public printCloudformationTemplate(template: Template): string {
63 return cloudform(template);
64 }
65
66 /*
67 * Private Helper Methods for Generating the Necessary CFN Specs for the CFN Template
68 */
69
70 /**
71 * Creates any Parmaters needed for the CFN Template
72 *
73 * @param databaseName - the name of the database being parsed.
74 * @returns the parameters for the template.
75 */
76 private makeParameters(databaseName: string) {
77 return {
78 [ResourceConstants.PARAMETERS.AppSyncApiName]: new StringParameter({
79 Description: `The name of the AppSync API generated from database ${databaseName}`,
80 Default: `AppSyncSimpleTransform`,
81 }),
82 [ResourceConstants.PARAMETERS.Env]: new StringParameter({
83 Description: 'The environment name. e.g. Dev, Test, or Production',
84 Default: 'NONE',
85 }),
86 [ResourceConstants.PARAMETERS.S3DeploymentBucket]: new StringParameter({
87 Description: 'The S3 bucket containing all deployment assets for the project.',
88 }),
89 [ResourceConstants.PARAMETERS.S3DeploymentRootKey]: new StringParameter({
90 Description: 'An S3 key relative to the S3DeploymentBucket that points to the root of the deployment directory.',
91 }),
92 [ResourceConstants.PARAMETERS.AppSyncApiId]: new StringParameter({
93 Description: 'The id of the AppSync API associated with this project.',
94 }),
95 [ResourceConstants.PARAMETERS.rdsRegion]: new StringParameter({
96 Description: 'The region that the RDS Cluster is located in.',
97 }),
98 [ResourceConstants.PARAMETERS.rdsClusterIdentifier]: new StringParameter({
99 Description: 'The ARN identifier denoting the RDS cluster.',
100 }),
101 [ResourceConstants.PARAMETERS.rdsSecretStoreArn]: new StringParameter({
102 Description: 'The ARN for the Secret containing the access for the RDS cluster.',
103 }),
104 [ResourceConstants.PARAMETERS.rdsDatabaseName]: new StringParameter({
105 Description: 'The name of the database within the RDS cluster to use.',
106 }),
107 };
108 }
109
110 /*
111 * Resources
112 */
113
114 /**
115 * Creates the IAM Role CFN Spec to allow AppSync to interact with the RDS cluster
116 *
117 * @returns the IAM role CloudFormation resource.
118 */
119 private makeIAMDataSourceRole() {
120 return new IAM.Role({
121 RoleName: Fn.Join('-', ['role', Fn.Ref(ResourceConstants.PARAMETERS.AppSyncApiId), Fn.Ref(ResourceConstants.PARAMETERS.Env)]),
122
123 AssumeRolePolicyDocument: {
124 Version: '2012-10-17',
125 Statement: [
126 {
127 Effect: 'Allow',
128 Principal: {
129 Service: 'appsync.amazonaws.com',
130 },
131 Action: 'sts:AssumeRole',
132 },
133 ],
134 },
135 Policies: [
136 new IAM.Role.Policy({
137 PolicyName: 'RelationalDatabaseAccessPolicy',
138 PolicyDocument: {
139 Version: '2012-10-17',
140 Statement: [
141 {
142 Effect: 'Allow',
143 Action: [
144 'rds-data:ExecuteSql',
145 'rds-data:ExecuteStatement',
146 'rds-data:DeleteItems',
147 'rds-data:GetItems',
148 'rds-data:InsertItems',
149 'rds-data:UpdateItems',
150 ],
151 Resource: [Fn.Ref(ResourceConstants.PARAMETERS.rdsClusterIdentifier)],
152 },
153 {
154 Effect: 'Allow',
155 Action: ['secretsmanager:GetSecretValue'],
156 Resource: [Fn.Ref(ResourceConstants.PARAMETERS.rdsSecretStoreArn)],
157 },
158 ],
159 },
160 }),
161 ],
162 });
163 }
164
165 /**
166 * Creates the AppSync DataSource CFN Spec pointing at the provided RDS Cluster
167 *
168 * @param cliContext - the Amplify context, used to load environment variables.
169 * @returns the data source CloudFormation resource.
170 */
171 private makeRelationalDataSource(cliContext: any): DataSource {
172 return new DataSource({
173 Type: 'RELATIONAL_DATABASE',
174 Name: `${this.context.databaseName}_rds_DataSource`,
175 Description: `RDS Data Source Provisioned for ${this.context.databaseName}`,
176 ApiId: Fn.Ref(ResourceConstants.PARAMETERS.AppSyncApiId),
177 ServiceRoleArn: Fn.GetAtt(ResourceConstants.RESOURCES.RelationalDatabaseAccessRole, 'Arn'),
178 RelationalDatabaseConfig: {
179 RelationalDatabaseSourceType: 'RDS_HTTP_ENDPOINT',
180 RdsHttpEndpointConfig: {
181 AwsRegion: Fn.Ref(ResourceConstants.PARAMETERS.rdsRegion),
182 DbClusterIdentifier: Fn.Ref(ResourceConstants.PARAMETERS.rdsClusterIdentifier),
183 DatabaseName: Fn.Ref(ResourceConstants.PARAMETERS.rdsDatabaseName),
184 Schema: this.context.databaseSchema,
185 AwsSecretStoreArn: Fn.Ref(ResourceConstants.PARAMETERS.rdsSecretStoreArn),
186 },
187 },
188 }).dependsOn([ResourceConstants.RESOURCES.RelationalDatabaseAccessRole]);
189 }
190}