Skip to content

Commit ed13014

Browse files
committed
feat(cli-internal): use cdk assets for vtl resolver overrides
1 parent 3b1be0c commit ed13014

3 files changed

Lines changed: 64 additions & 35 deletions

File tree

packages/amplify-cli/src/__tests__/commands/gen2-migration/generate/amplify/data/data.generator.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ describe('DataGenerator', () => {
414414
mockReaddirSync.mockReturnValue(['Query.listProducts.res.vtl']);
415415

416416
const addImportSpy = jest.spyOn(backendGenerator, 'addImport');
417+
const addNamespaceImportSpy = jest.spyOn(backendGenerator, 'addNamespaceImport');
417418
const addStatementSpy = jest.spyOn(backendGenerator, 'addStatement');
418419

419420
const generator = new DataGenerator(gen1App, backendGenerator, outputDir, {
@@ -425,10 +426,11 @@ describe('DataGenerator', () => {
425426
const ops = await generator.plan();
426427
await ops[0].execute();
427428

428-
// Resolver imports: fs, path, url
429-
expect(addImportSpy).toHaveBeenCalledWith('fs', ['readFileSync', 'readdirSync']);
429+
// Resolver imports: fs (readdirSync only), path, url
430+
expect(addImportSpy).toHaveBeenCalledWith('fs', ['readdirSync']);
430431
expect(addImportSpy).toHaveBeenCalledWith('path', ['join', 'dirname']);
431432
expect(addImportSpy).toHaveBeenCalledWith('url', ['fileURLToPath']);
433+
expect(addNamespaceImportSpy).toHaveBeenCalledWith('aws-cdk-lib/aws-s3-assets', 'assets');
432434

433435
// 4 statements: __dirname, resolversDir, resolverFiles, for-of loop
434436
expect(addStatementSpy).toHaveBeenCalledTimes(4);

packages/amplify-cli/src/commands/gen2-migration/generate/amplify/backend.generator.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const factory = ts.factory;
1818
*/
1919
export class BackendGenerator implements Planner {
2020
private readonly imports: Array<{ readonly source: string; readonly identifiers: string[] }> = [];
21+
private readonly namespaceImports: Array<{ readonly source: string; readonly alias: string }> = [];
2122
private readonly defineBackendProperties: ts.ObjectLiteralElementLike[] = [];
2223
private readonly postDefineStatements: ts.Statement[] = [];
2324
private readonly earlyStatements: ts.Statement[] = [];
@@ -46,6 +47,15 @@ export class BackendGenerator implements Planner {
4647
}
4748
}
4849

50+
/**
51+
* Adds a namespace import (`import * as alias from 'source'`) to backend.ts.
52+
*/
53+
public addNamespaceImport(source: string, alias: string): void {
54+
if (!this.namespaceImports.some((i) => i.source === source)) {
55+
this.namespaceImports.push({ source, alias });
56+
}
57+
}
58+
4959
/**
5060
* Adds a property to the `defineBackend({ ... })` call.
5161
*/
@@ -122,6 +132,12 @@ export class BackendGenerator implements Planner {
122132
nodes.push(createImportDeclaration(imp.source, imp.identifiers));
123133
}
124134

135+
// Namespace imports (import * as X from 'source'), sorted by importOrder
136+
const sortedNamespaceImports = [...this.namespaceImports].sort((a, b) => importOrder(a.source) - importOrder(b.source));
137+
for (const ns of sortedNamespaceImports) {
138+
nodes.push(createNamespaceImportDeclaration(ns.source, ns.alias));
139+
}
140+
125141
// Sort defineBackend properties: auth first, then data, storage, then functions
126142
const sortedProperties = [...this.defineBackendProperties].sort((a, b) => {
127143
const getName = (prop: ts.ObjectLiteralElementLike): string => {
@@ -186,6 +202,14 @@ function createImportDeclaration(source: string, identifiers: string[]): ts.Impo
186202
);
187203
}
188204

205+
function createNamespaceImportDeclaration(source: string, alias: string): ts.ImportDeclaration {
206+
return factory.createImportDeclaration(
207+
undefined,
208+
factory.createImportClause(false, undefined, factory.createNamespaceImport(factory.createIdentifier(alias))),
209+
factory.createStringLiteral(source),
210+
);
211+
}
212+
189213
/**
190214
* Returns a numeric sort key for import source paths.
191215
*

packages/amplify-cli/src/commands/gen2-migration/generate/amplify/data/data.generator.ts

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -311,15 +311,16 @@ export class DataGenerator implements Planner {
311311
/**
312312
* Contributes resolver override statements to backend.ts.
313313
*
314-
* Generates code that reads VTL files from `data/resolvers/` at runtime
315-
* and overrides the pipeline function response mapping templates, replacing
316-
* the S3-based templates with inline content.
314+
* Generates code that reads VTL files from `data/resolvers/` at deploy
315+
* time, uploads them as CDK Assets to S3, and overrides the pipeline
316+
* function mapping template S3 locations.
317317
*/
318318
private contributeResolverOverrides(): void {
319319
// Imports for the generated backend.ts file
320-
this.backendGenerator.addImport('fs', ['readFileSync', 'readdirSync']);
320+
this.backendGenerator.addImport('fs', ['readdirSync']);
321321
this.backendGenerator.addImport('path', ['join', 'dirname']);
322322
this.backendGenerator.addImport('url', ['fileURLToPath']);
323+
this.backendGenerator.addNamespaceImport('aws-cdk-lib/aws-s3-assets', 'assets');
323324

324325
// const __dirname = dirname(fileURLToPath(import.meta.url));
325326
this.backendGenerator.addStatement(
@@ -399,13 +400,12 @@ export class DataGenerator implements Planner {
399400
* const functionId = `${typeName}${fieldName.charAt(0).toUpperCase() + fieldName.slice(1)}DataResolverFn`;
400401
* const pipelineFunction = backend.data.resources.cfnResources.cfnFunctionConfigurations[functionId];
401402
* if (pipelineFunction) {
402-
* const template = readFileSync(join(resolversDir, file), "utf8");
403+
* const templatePath = join(resolversDir, file);
404+
* const vtlTemplate = new assets.Asset(backend.data, `VTLTemplate-${file}`, { path: templatePath });
403405
* if (isRequest) {
404-
* pipelineFunction.requestMappingTemplateS3Location = undefined;
405-
* pipelineFunction.requestMappingTemplate = template;
406+
* pipelineFunction.requestMappingTemplateS3Location = vtlTemplate.s3ObjectUrl;
406407
* } else {
407-
* pipelineFunction.responseMappingTemplateS3Location = undefined;
408-
* pipelineFunction.responseMappingTemplate = template;
408+
* pipelineFunction.responseMappingTemplateS3Location = vtlTemplate.s3ObjectUrl;
409409
* }
410410
* }
411411
* }
@@ -530,17 +530,32 @@ export class DataGenerator implements Planner {
530530
factory.createIdentifier('pipelineFunction'),
531531
factory.createBlock(
532532
[
533-
// const template = readFileSync(join(resolversDir, file), "utf8");
533+
// const templatePath = join(resolversDir, file);
534534
TS.constDecl(
535-
'template',
536-
factory.createCallExpression(factory.createIdentifier('readFileSync'), undefined, [
537-
factory.createCallExpression(factory.createIdentifier('join'), undefined, [
538-
factory.createIdentifier('resolversDir'),
539-
factory.createIdentifier('file'),
540-
]),
541-
factory.createStringLiteral('utf8'),
535+
'templatePath',
536+
factory.createCallExpression(factory.createIdentifier('join'), undefined, [
537+
factory.createIdentifier('resolversDir'),
538+
factory.createIdentifier('file'),
542539
]),
543540
),
541+
// const vtlTemplate = new assets.Asset(backend.data, `VTLTemplate-${file}`, { path: templatePath });
542+
TS.constDecl(
543+
'vtlTemplate',
544+
factory.createNewExpression(
545+
factory.createPropertyAccessExpression(factory.createIdentifier('assets'), factory.createIdentifier('Asset')),
546+
undefined,
547+
[
548+
TS.propAccess('backend', 'data') as ts.Expression,
549+
factory.createTemplateExpression(factory.createTemplateHead('VTLTemplate-'), [
550+
factory.createTemplateSpan(factory.createIdentifier('file'), factory.createTemplateTail('')),
551+
]),
552+
factory.createObjectLiteralExpression(
553+
[factory.createPropertyAssignment('path', factory.createIdentifier('templatePath'))],
554+
false,
555+
),
556+
],
557+
),
558+
),
544559
// if (isRequest) { ... } else { ... }
545560
factory.createIfStatement(
546561
factory.createIdentifier('isRequest'),
@@ -552,16 +567,10 @@ export class DataGenerator implements Planner {
552567
factory.createIdentifier('pipelineFunction'),
553568
factory.createIdentifier('requestMappingTemplateS3Location'),
554569
),
555-
factory.createIdentifier('undefined'),
556-
),
557-
),
558-
factory.createExpressionStatement(
559-
factory.createAssignment(
560570
factory.createPropertyAccessExpression(
561-
factory.createIdentifier('pipelineFunction'),
562-
factory.createIdentifier('requestMappingTemplate'),
571+
factory.createIdentifier('vtlTemplate'),
572+
factory.createIdentifier('s3ObjectUrl'),
563573
),
564-
factory.createIdentifier('template'),
565574
),
566575
),
567576
],
@@ -575,16 +584,10 @@ export class DataGenerator implements Planner {
575584
factory.createIdentifier('pipelineFunction'),
576585
factory.createIdentifier('responseMappingTemplateS3Location'),
577586
),
578-
factory.createIdentifier('undefined'),
579-
),
580-
),
581-
factory.createExpressionStatement(
582-
factory.createAssignment(
583587
factory.createPropertyAccessExpression(
584-
factory.createIdentifier('pipelineFunction'),
585-
factory.createIdentifier('responseMappingTemplate'),
588+
factory.createIdentifier('vtlTemplate'),
589+
factory.createIdentifier('s3ObjectUrl'),
586590
),
587-
factory.createIdentifier('template'),
588591
),
589592
),
590593
],

0 commit comments

Comments
 (0)