1 | "use strict";
|
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
4 | };
|
5 | Object.defineProperty(exports, "__esModule", { value: true });
|
6 | const table_1 = require("cloudform-types/types/dynamoDb/table");
|
7 | const resolver_1 = __importDefault(require("cloudform-types/types/appSync/resolver"));
|
8 | const cloudform_types_1 = require("cloudform-types");
|
9 | const graphql_mapping_template_1 = require("graphql-mapping-template");
|
10 | const graphql_transformer_common_1 = require("graphql-transformer-common");
|
11 | const graphql_transformer_core_1 = require("graphql-transformer-core");
|
12 | class ResourceFactory {
|
13 | makeParams() {
|
14 | return {};
|
15 | }
|
16 | initTemplate() {
|
17 | return {
|
18 | Parameters: this.makeParams(),
|
19 | Resources: {},
|
20 | Outputs: {},
|
21 | };
|
22 | }
|
23 | updateTableForConnection(table, connectionName, connectionAttributeName, sortField = null) {
|
24 | const gsis = table.Properties.GlobalSecondaryIndexes || [];
|
25 | if (gsis.length >= 20) {
|
26 | throw new graphql_transformer_core_1.InvalidDirectiveError(`Cannot create connection ${connectionName}. Table ${table.Properties.TableName} out of GSI capacity.`);
|
27 | }
|
28 | const connectionGSIName = `gsi-${connectionName}`;
|
29 | const existingGSI = gsis.find(gsi => gsi.IndexName === connectionGSIName);
|
30 | if (!existingGSI) {
|
31 | const keySchema = [new table_1.KeySchema({ AttributeName: connectionAttributeName, KeyType: 'HASH' })];
|
32 | if (sortField) {
|
33 | keySchema.push(new table_1.KeySchema({ AttributeName: sortField.name, KeyType: 'RANGE' }));
|
34 | }
|
35 | gsis.push(new table_1.GlobalSecondaryIndex({
|
36 | IndexName: connectionGSIName,
|
37 | KeySchema: keySchema,
|
38 | Projection: new table_1.Projection({
|
39 | ProjectionType: 'ALL',
|
40 | }),
|
41 | ProvisionedThroughput: cloudform_types_1.Fn.If(graphql_transformer_common_1.ResourceConstants.CONDITIONS.ShouldUsePayPerRequestBilling, cloudform_types_1.Refs.NoValue, {
|
42 | ReadCapacityUnits: cloudform_types_1.Fn.Ref(graphql_transformer_common_1.ResourceConstants.PARAMETERS.DynamoDBModelTableReadIOPS),
|
43 | WriteCapacityUnits: cloudform_types_1.Fn.Ref(graphql_transformer_common_1.ResourceConstants.PARAMETERS.DynamoDBModelTableWriteIOPS),
|
44 | }),
|
45 | }));
|
46 | }
|
47 | const attributeDefinitions = table.Properties.AttributeDefinitions;
|
48 | const existingAttribute = attributeDefinitions.find(attr => attr.AttributeName === connectionAttributeName);
|
49 | if (!existingAttribute) {
|
50 | attributeDefinitions.push(new table_1.AttributeDefinition({
|
51 | AttributeName: connectionAttributeName,
|
52 | AttributeType: 'S',
|
53 | }));
|
54 | }
|
55 | if (sortField) {
|
56 | const existingSortAttribute = attributeDefinitions.find(attr => attr.AttributeName === sortField.name);
|
57 | if (!existingSortAttribute) {
|
58 | const scalarType = graphql_transformer_common_1.DEFAULT_SCALARS[sortField.type];
|
59 | const attributeType = scalarType === 'String' ? 'S' : 'N';
|
60 | attributeDefinitions.push(new table_1.AttributeDefinition({ AttributeName: sortField.name, AttributeType: attributeType }));
|
61 | }
|
62 | }
|
63 | table.Properties.GlobalSecondaryIndexes = gsis;
|
64 | table.Properties.AttributeDefinitions = attributeDefinitions;
|
65 | return table;
|
66 | }
|
67 | makeGetItemConnectionResolver(type, field, relatedType, connectionAttribute, idFieldName, sortFieldInfo) {
|
68 | let keyObj = graphql_mapping_template_1.obj({
|
69 | [`${idFieldName}`]: graphql_mapping_template_1.ref(`util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.${connectionAttribute}, "${graphql_transformer_common_1.NONE_VALUE}"))`),
|
70 | });
|
71 | if (sortFieldInfo) {
|
72 | if (sortFieldInfo.sortFieldIsStringLike) {
|
73 | keyObj.attributes.push([
|
74 | sortFieldInfo.primarySortFieldName,
|
75 | graphql_mapping_template_1.ref(`util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.${sortFieldInfo.sortFieldName}, "${graphql_transformer_common_1.NONE_VALUE}"))`),
|
76 | ]);
|
77 | }
|
78 | else {
|
79 | keyObj.attributes.push([
|
80 | sortFieldInfo.primarySortFieldName,
|
81 | graphql_mapping_template_1.ref(`util.dynamodb.toDynamoDBJson($util.defaultIfNull($ctx.source.${sortFieldInfo.sortFieldName}, "${graphql_transformer_common_1.NONE_INT_VALUE}"))`),
|
82 | ]);
|
83 | }
|
84 | }
|
85 | return new resolver_1.default({
|
86 | ApiId: cloudform_types_1.Fn.GetAtt(graphql_transformer_common_1.ResourceConstants.RESOURCES.GraphQLAPILogicalID, 'ApiId'),
|
87 | DataSourceName: cloudform_types_1.Fn.GetAtt(graphql_transformer_common_1.ModelResourceIDs.ModelTableDataSourceID(relatedType), 'Name'),
|
88 | FieldName: field,
|
89 | TypeName: type,
|
90 | RequestMappingTemplate: graphql_mapping_template_1.print(graphql_mapping_template_1.DynamoDBMappingTemplate.getItem({
|
91 | key: keyObj,
|
92 | })),
|
93 | ResponseMappingTemplate: graphql_mapping_template_1.print(graphql_mapping_template_1.ref('util.toJson($context.result)')),
|
94 | }).dependsOn(graphql_transformer_common_1.ResourceConstants.RESOURCES.GraphQLSchemaLogicalID);
|
95 | }
|
96 | makeQueryConnectionResolver(type, field, relatedType, connectionAttribute, connectionName, idFieldName, sortKeyInfo, limit) {
|
97 | const pageLimit = limit || graphql_transformer_common_1.ResourceConstants.DEFAULT_PAGE_LIMIT;
|
98 | const setup = [
|
99 | graphql_mapping_template_1.set(graphql_mapping_template_1.ref('limit'), graphql_mapping_template_1.ref(`util.defaultIfNull($context.args.limit, ${pageLimit})`)),
|
100 | graphql_mapping_template_1.set(graphql_mapping_template_1.ref('query'), graphql_mapping_template_1.obj({
|
101 | expression: graphql_mapping_template_1.str('#connectionAttribute = :connectionAttribute'),
|
102 | expressionNames: graphql_mapping_template_1.obj({
|
103 | '#connectionAttribute': graphql_mapping_template_1.str(connectionAttribute),
|
104 | }),
|
105 | expressionValues: graphql_mapping_template_1.obj({
|
106 | ':connectionAttribute': graphql_mapping_template_1.obj({
|
107 | S: graphql_mapping_template_1.str(`$context.source.${idFieldName}`),
|
108 | }),
|
109 | }),
|
110 | })),
|
111 | ];
|
112 | if (sortKeyInfo) {
|
113 | setup.push(graphql_transformer_common_1.applyKeyConditionExpression(sortKeyInfo.fieldName, sortKeyInfo.attributeType, 'query'));
|
114 | }
|
115 | return new resolver_1.default({
|
116 | ApiId: cloudform_types_1.Fn.GetAtt(graphql_transformer_common_1.ResourceConstants.RESOURCES.GraphQLAPILogicalID, 'ApiId'),
|
117 | DataSourceName: cloudform_types_1.Fn.GetAtt(graphql_transformer_common_1.ModelResourceIDs.ModelTableDataSourceID(relatedType), 'Name'),
|
118 | FieldName: field,
|
119 | TypeName: type,
|
120 | RequestMappingTemplate: graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([
|
121 | ...setup,
|
122 | graphql_mapping_template_1.DynamoDBMappingTemplate.query({
|
123 | query: graphql_mapping_template_1.raw('$util.toJson($query)'),
|
124 | scanIndexForward: graphql_mapping_template_1.ifElse(graphql_mapping_template_1.ref('context.args.sortDirection'), graphql_mapping_template_1.ifElse(graphql_mapping_template_1.equals(graphql_mapping_template_1.ref('context.args.sortDirection'), graphql_mapping_template_1.str('ASC')), graphql_mapping_template_1.bool(true), graphql_mapping_template_1.bool(false)), graphql_mapping_template_1.bool(true)),
|
125 | filter: graphql_mapping_template_1.ifElse(graphql_mapping_template_1.ref('context.args.filter'), graphql_mapping_template_1.ref('util.transform.toDynamoDBFilterExpression($ctx.args.filter)'), graphql_mapping_template_1.nul()),
|
126 | limit: graphql_mapping_template_1.ref('limit'),
|
127 | nextToken: graphql_mapping_template_1.ifElse(graphql_mapping_template_1.ref('context.args.nextToken'), graphql_mapping_template_1.ref('util.toJson($context.args.nextToken)'), graphql_mapping_template_1.nul()),
|
128 | index: graphql_mapping_template_1.str(`gsi-${connectionName}`),
|
129 | }),
|
130 | ])),
|
131 | ResponseMappingTemplate: graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([graphql_mapping_template_1.iff(graphql_mapping_template_1.raw('!$result'), graphql_mapping_template_1.set(graphql_mapping_template_1.ref('result'), graphql_mapping_template_1.ref('ctx.result'))), graphql_mapping_template_1.raw('$util.toJson($result)')])),
|
132 | }).dependsOn(graphql_transformer_common_1.ResourceConstants.RESOURCES.GraphQLSchemaLogicalID);
|
133 | }
|
134 | makeGetItemConnectionWithKeyResolver(type, field, relatedType, connectionAttributes, keySchema) {
|
135 | const partitionKeyName = keySchema[0].AttributeName;
|
136 | let keyObj = graphql_mapping_template_1.obj({
|
137 | [partitionKeyName]: graphql_mapping_template_1.ref(`util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.${connectionAttributes[0]}, "${graphql_transformer_common_1.NONE_VALUE}"))`),
|
138 | });
|
139 | if (connectionAttributes.length > 2) {
|
140 | const rangeKeyFields = connectionAttributes.slice(1);
|
141 | const sortKeyName = keySchema[1].AttributeName;
|
142 | const condensedSortKeyValue = this.condenseRangeKey(rangeKeyFields.map(keyField => `\${ctx.source.${keyField}}`));
|
143 | keyObj.attributes.push([
|
144 | sortKeyName,
|
145 | graphql_mapping_template_1.ref(`util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank("${condensedSortKeyValue}", "${graphql_transformer_common_1.NONE_VALUE}"))`),
|
146 | ]);
|
147 | }
|
148 | else if (connectionAttributes[1]) {
|
149 | const sortKeyName = keySchema[1].AttributeName;
|
150 | keyObj.attributes.push([
|
151 | sortKeyName,
|
152 | graphql_mapping_template_1.ref(`util.dynamodb.toDynamoDBJson($util.defaultIfNullOrBlank($ctx.source.${connectionAttributes[1]}, "${graphql_transformer_common_1.NONE_VALUE}"))`),
|
153 | ]);
|
154 | }
|
155 | return new resolver_1.default({
|
156 | ApiId: cloudform_types_1.Fn.GetAtt(graphql_transformer_common_1.ResourceConstants.RESOURCES.GraphQLAPILogicalID, 'ApiId'),
|
157 | DataSourceName: cloudform_types_1.Fn.GetAtt(graphql_transformer_common_1.ModelResourceIDs.ModelTableDataSourceID(relatedType), 'Name'),
|
158 | FieldName: field,
|
159 | TypeName: type,
|
160 | RequestMappingTemplate: graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([
|
161 | graphql_mapping_template_1.DynamoDBMappingTemplate.getItem({
|
162 | key: keyObj,
|
163 | }),
|
164 | ])),
|
165 | ResponseMappingTemplate: graphql_mapping_template_1.print(graphql_mapping_template_1.ref('util.toJson($context.result)')),
|
166 | }).dependsOn(graphql_transformer_common_1.ResourceConstants.RESOURCES.GraphQLSchemaLogicalID);
|
167 | }
|
168 | makeQueryConnectionWithKeyResolver(type, field, relatedType, connectionAttributes, keySchema, indexName, limit) {
|
169 | const pageLimit = limit || graphql_transformer_common_1.ResourceConstants.DEFAULT_PAGE_LIMIT;
|
170 | const setup = [
|
171 | graphql_mapping_template_1.set(graphql_mapping_template_1.ref('limit'), graphql_mapping_template_1.ref(`util.defaultIfNull($context.args.limit, ${pageLimit})`)),
|
172 | graphql_mapping_template_1.set(graphql_mapping_template_1.ref('query'), this.makeExpression(keySchema, connectionAttributes)),
|
173 | ];
|
174 | if (keySchema[1] && !connectionAttributes[1]) {
|
175 | const sortKeyField = relatedType.fields.find(f => f.name.value === keySchema[1].AttributeName);
|
176 | if (sortKeyField) {
|
177 | setup.push(graphql_transformer_common_1.applyKeyConditionExpression(String(keySchema[1].AttributeName), graphql_transformer_common_1.attributeTypeFromScalar(sortKeyField.type), 'query'));
|
178 | }
|
179 | else {
|
180 | setup.push(graphql_transformer_common_1.applyCompositeKeyConditionExpression(this.getSortKeyNames(String(keySchema[1].AttributeName)), 'query', this.makeCompositeSortKeyName(String(keySchema[1].AttributeName)), String(keySchema[1].AttributeName)));
|
181 | }
|
182 | }
|
183 | let queryArguments = {
|
184 | query: graphql_mapping_template_1.raw('$util.toJson($query)'),
|
185 | scanIndexForward: graphql_mapping_template_1.ifElse(graphql_mapping_template_1.ref('context.args.sortDirection'), graphql_mapping_template_1.ifElse(graphql_mapping_template_1.equals(graphql_mapping_template_1.ref('context.args.sortDirection'), graphql_mapping_template_1.str('ASC')), graphql_mapping_template_1.bool(true), graphql_mapping_template_1.bool(false)), graphql_mapping_template_1.bool(true)),
|
186 | filter: graphql_mapping_template_1.ifElse(graphql_mapping_template_1.ref('context.args.filter'), graphql_mapping_template_1.ref('util.transform.toDynamoDBFilterExpression($ctx.args.filter)'), graphql_mapping_template_1.nul()),
|
187 | limit: graphql_mapping_template_1.ref('limit'),
|
188 | nextToken: graphql_mapping_template_1.ifElse(graphql_mapping_template_1.ref('context.args.nextToken'), graphql_mapping_template_1.ref('util.toJson($context.args.nextToken)'), graphql_mapping_template_1.nul()),
|
189 | index: indexName ? graphql_mapping_template_1.str(indexName) : undefined,
|
190 | };
|
191 | if (!indexName) {
|
192 | const indexArg = 'index';
|
193 | delete queryArguments[indexArg];
|
194 | }
|
195 | const queryObj = graphql_mapping_template_1.DynamoDBMappingTemplate.query(queryArguments);
|
196 | return new resolver_1.default({
|
197 | ApiId: cloudform_types_1.Fn.GetAtt(graphql_transformer_common_1.ResourceConstants.RESOURCES.GraphQLAPILogicalID, 'ApiId'),
|
198 | DataSourceName: cloudform_types_1.Fn.GetAtt(graphql_transformer_common_1.ModelResourceIDs.ModelTableDataSourceID(relatedType.name.value), 'Name'),
|
199 | FieldName: field,
|
200 | TypeName: type,
|
201 | RequestMappingTemplate: graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([...setup, queryObj])),
|
202 | ResponseMappingTemplate: graphql_mapping_template_1.print(graphql_mapping_template_1.compoundExpression([graphql_mapping_template_1.iff(graphql_mapping_template_1.raw('!$result'), graphql_mapping_template_1.set(graphql_mapping_template_1.ref('result'), graphql_mapping_template_1.ref('ctx.result'))), graphql_mapping_template_1.raw('$util.toJson($result)')])),
|
203 | }).dependsOn(graphql_transformer_common_1.ResourceConstants.RESOURCES.GraphQLSchemaLogicalID);
|
204 | }
|
205 | makeExpression(keySchema, connectionAttributes) {
|
206 | if (keySchema[1] && connectionAttributes[1]) {
|
207 | let condensedSortKeyValue = undefined;
|
208 | if (connectionAttributes.length > 2) {
|
209 | const rangeKeyFields = connectionAttributes.slice(1);
|
210 | condensedSortKeyValue = this.condenseRangeKey(rangeKeyFields.map(keyField => `\${context.source.${keyField}}`));
|
211 | }
|
212 | return graphql_mapping_template_1.obj({
|
213 | expression: graphql_mapping_template_1.str('#partitionKey = :partitionKey AND #sortKey = :sortKey'),
|
214 | expressionNames: graphql_mapping_template_1.obj({
|
215 | '#partitionKey': graphql_mapping_template_1.str(String(keySchema[0].AttributeName)),
|
216 | '#sortKey': graphql_mapping_template_1.str(String(keySchema[1].AttributeName)),
|
217 | }),
|
218 | expressionValues: graphql_mapping_template_1.obj({
|
219 | ':partitionKey': graphql_mapping_template_1.obj({
|
220 | S: graphql_mapping_template_1.str(`$context.source.${connectionAttributes[0]}`),
|
221 | }),
|
222 | ':sortKey': graphql_mapping_template_1.obj({
|
223 | S: graphql_mapping_template_1.str(condensedSortKeyValue || `$context.source.${connectionAttributes[1]}`),
|
224 | }),
|
225 | }),
|
226 | });
|
227 | }
|
228 | return graphql_mapping_template_1.obj({
|
229 | expression: graphql_mapping_template_1.str('#partitionKey = :partitionKey'),
|
230 | expressionNames: graphql_mapping_template_1.obj({
|
231 | '#partitionKey': graphql_mapping_template_1.str(String(keySchema[0].AttributeName)),
|
232 | }),
|
233 | expressionValues: graphql_mapping_template_1.obj({
|
234 | ':partitionKey': graphql_mapping_template_1.obj({
|
235 | S: graphql_mapping_template_1.str(`$context.source.${connectionAttributes[0]}`),
|
236 | }),
|
237 | }),
|
238 | });
|
239 | }
|
240 | condenseRangeKey(fields) {
|
241 | return fields.join(graphql_transformer_common_1.ModelResourceIDs.ModelCompositeKeySeparator());
|
242 | }
|
243 | makeCompositeSortKeyName(sortKeyName) {
|
244 | const attributeNames = sortKeyName.split(graphql_transformer_common_1.ModelResourceIDs.ModelCompositeKeySeparator());
|
245 | return graphql_transformer_common_1.toCamelCase(attributeNames);
|
246 | }
|
247 | getSortKeyNames(compositeSK) {
|
248 | return compositeSK.split(graphql_transformer_common_1.ModelResourceIDs.ModelCompositeKeySeparator());
|
249 | }
|
250 | }
|
251 | exports.ResourceFactory = ResourceFactory;
|
252 |
|
\ | No newline at end of file |