Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/amplify-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"amplify-nodejs-function-runtime-provider": "2.5.31",
"amplify-python-function-runtime-provider": "2.4.53",
"aws-cdk-lib": "~2.189.1",
"cdk-from-cfn": "0.258.0",
"chalk": "^4.1.1",
"ci-info": "^3.8.0",
"cli-table3": "^0.6.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface BackendRenderParameters {
importFrom: string;
functionNamesAndCategories: Map<string, string>;
};
analytics?: any[];
customResources?: Map<string, string>;
unsupportedCategories?: Map<string, string>;
}
Expand Down Expand Up @@ -751,6 +752,9 @@ export class BackendSynthesizer {

imports.push(this.createImportStatement([backendFunctionIdentifier], '@aws-amplify/backend'));

// if (renderArgs.analytics) {
// imports.push('// analytics tbd');
// }
if (renderArgs.unsupportedCategories) {
const categories = renderArgs.unsupportedCategories;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import assert from 'node:assert';
import { BackendEnvironmentResolver } from './backend_environment_selector';
import { StateManager } from '@aws-amplify/amplify-cli-core';

export interface AppAnalyticsDefinitionFetcher {
getDefinition(): Promise<any | undefined>; // TODO: i guess we need AnalyticsDefinition to be handwritten...
}

export class AppAnalyticsDefinitionFetcher {
constructor(private backendEnvironmentResolver: BackendEnvironmentResolver, private stateManager: StateManager) {}

getDefinition = async (): Promise<any | undefined> => {
const backendEnvironment = await this.backendEnvironmentResolver.selectBackendEnvironment();
assert(backendEnvironment?.stackName);

const meta = this.stateManager.getMeta();
const analytics = meta?.analytics ?? {};
return analytics;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { AppFunctionsDefinitionFetcher } from './app_functions_definition_fetche
import { printer } from './printer';
import { format } from './format';
import ora from 'ora';
import { AppAnalyticsDefinitionFetcher } from './app_analytics_definition_fetcher';

interface CodegenCommandParameters {
analytics: Analytics;
Expand All @@ -41,6 +42,7 @@ interface CodegenCommandParameters {
authDefinitionFetcher: AppAuthDefinitionFetcher;
storageDefinitionFetcher: AppStorageDefinitionFetcher;
functionsDefinitionFetcher: AppFunctionsDefinitionFetcher;
analyticsDefinitionFetcher: AppAnalyticsDefinitionFetcher;
}

const TEMP_GEN_2_OUTPUT_DIR = 'amplify-gen2';
Expand Down Expand Up @@ -74,18 +76,21 @@ const generateGen2Code = async ({
dataDefinitionFetcher,
storageDefinitionFetcher,
functionsDefinitionFetcher,
analyticsDefinitionFetcher,
}: CodegenCommandParameters) => {
const fetchingAWSResourceDetails = ora('Fetching resource details from AWS').start();
const auth = await authDefinitionFetcher.getDefinition();
const storage = await storageDefinitionFetcher.getDefinition();
const data = await dataDefinitionFetcher.getDefinition();
const functions = await functionsDefinitionFetcher.getDefinition();
const analytics = await analyticsDefinitionFetcher.getDefinition();

console.log('Auth:', auth ? 'EXISTS' : 'UNDEFINED');
console.log('Storage:', storage ? 'EXISTS' : 'UNDEFINED');
console.log('Data:', data ? JSON.stringify(data, null, 2) : 'UNDEFINED');
console.log('Functions:', functions ? `${functions.length} functions` : 'UNDEFINED');
console.log('Backend env:', backendEnvironmentName);
console.log('Analytics', analytics ? JSON.stringify(analytics, null, 2) : 'UNDEFINED');

const gen2RenderOptions = {
outputDir: outputDirectory,
Expand All @@ -94,6 +99,7 @@ const generateGen2Code = async ({
storage,
data,
functions,
analytics,
customResources: await getCustomResourceMap(),
unsupportedCategories: unsupportedCategories(),
};
Expand Down Expand Up @@ -216,6 +222,7 @@ const unsupportedCategories = (): Map<string, string> => {
const unsupportedCategories = new Map<string, string>();
const urlPrefix = 'https://docs.amplify.aws/react/build-a-backend/add-aws-services';
const restAPIKey = 'rest api';
const analyticsKey = 'analytics';

unsupportedCategories.set('geo', `${urlPrefix}/geo/`);
unsupportedCategories.set('analytics', `${urlPrefix}/analytics/`);
Expand All @@ -242,6 +249,16 @@ const unsupportedCategories = (): Map<string, string> => {
}
});
}
} else if (category === 'analytics') {
const analytics = meta?.analytics ?? {};
Object.keys(analytics).forEach((analytic) => {
const analyticObj = analytics[analytic];
if (analyticObj.service === 'Pinpoint') {
const analyticsDocLink = unsupportedCategories.get(analyticsKey);
assert(analyticsDocLink);
unsupportedCategoriesList.set(`Pinpoint ${analyticsKey}`, analyticsDocLink);
}
});
} else {
if (unsupportedCategories.has(category) && Object.entries(meta[category]).length > 0) {
const unsupportedCategoryDocLink = unsupportedCategories.get(category);
Expand Down Expand Up @@ -527,6 +544,7 @@ export async function prepare() {
backendEnvironmentResolver,
stateManager,
),
analyticsDefinitionFetcher: new AppAnalyticsDefinitionFetcher(backendEnvironmentResolver, stateManager),
analytics: new AppAnalytics(appId),
logger: new AppContextLogger(appId),
backendEnvironmentName: backendEnvironment?.environmentName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import { DataDefinition, DataTableMapping, generateDataSource } from '../generat

import { FunctionDefinition, renderFunctions } from '../generators/functions/index';
import assert from 'assert';
import { CdkFromCfn } from '../unsupported/cdk-fron-cfn';

/**
* Configuration options for Gen 2 rendering pipeline
Expand Down Expand Up @@ -90,6 +91,8 @@ export interface Gen2RenderingOptions {
/** Lambda function definitions */
functions?: FunctionDefinition[];

analytics?: any;

/** Custom CloudFormation resources that need manual migration */
customResources?: Map<string, string>;

Expand Down Expand Up @@ -127,6 +130,7 @@ export const createGen2Renderer = ({
storage,
data,
functions,
analytics,
customResources,
unsupportedCategories,
fileWriter = (content, path) => createFileWriter(path)(content),
Expand Down Expand Up @@ -196,6 +200,24 @@ export const createGen2Renderer = ({
backendRenderOptions.unsupportedCategories = unsupportedCategories;
}

if (analytics) {
console.log('There are Analytics found in the Gen1 App');
// TODO: this should be instantiated earlier when more unsupported categories are added
const cdkFromCfn = new CdkFromCfn(outputDir, fileWriter);
// eslint-disable-next-line @typescript-eslint/no-misused-promises
Object.keys(analytics).forEach(async (analytic) => {
const analyticObj = analytics[analytic];
analyticObj.name = analytic;
if (analyticObj.service === 'Kinesis') {
console.log('Analytics backed by Kinesis found, generating L1 Code');
await cdkFromCfn.generateKinesisAnalyticsL1Code(analyticObj);
} else {
console.log('Analytics backed by Pinpoint found, still unsupported');
}
});
backendRenderOptions.analytics = analytics;
}

// Process Lambda functions - create resource.ts and handler.ts files
if (functions && functions.length) {
const functionNamesAndCategory = new Map<string, string>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as path from 'path';
import * as fs from 'fs/promises';
import * as cdk_from_cfn from 'cdk-from-cfn';
import { CFNTemplate } from '../../../refactor/types';
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';

export class CdkFromCfn {
public constructor(private readonly dir: string, private readonly fileWriter: (content, path) => Promise<void>) {}

public async generateKinesisAnalyticsL1Code(definition: any): Promise<void> {
const filePath = path.join(this.dir, 'amplify', 'analytics', `${definition.name}-stack.ts`);
const templateS3Url = definition.providerMetadata.s3TemplateURL;
const template = await getS3ObjectContent(templateS3Url);
const stackName = definition.providerMetadata.logicalId;

const finalTemplate = this.preTransmute(template);
const tsFile = cdk_from_cfn.transmute(JSON.stringify(finalTemplate), 'typescript', stackName);
await fs.mkdir(path.dirname(filePath), { recursive: true });
await this.fileWriter(tsFile, filePath);
}

private preTransmute(template: CFNTemplate): CFNTemplate {
// Rename "env" parameter to "amplify-env"
if (template.Parameters?.env) {
template.Parameters['amplify-env'] = template.Parameters.env;
delete template.Parameters.env;
}

// Update all Ref references from "env" to "amplify-env"
const updateRefs = (obj: any): void => {
if (typeof obj === 'object' && obj !== null) {
if (obj.Ref === 'env') {
obj.Ref = 'amplify-env';
}
Object.values(obj).forEach(updateRefs);
}
};

updateRefs(template.Resources);

// Resolve CFN conditions first
// new CFNConditionResolver(template).resolve(template.Parameters);

return template;
}
}

async function getS3ObjectContent(s3Url: string): Promise<CFNTemplate> {
const url = new URL(s3Url);
const splitPath = url.pathname.split('/');
const bucket = splitPath[1];
const key = splitPath.slice(2).join('/');
console.log('bucket', bucket, 'key', key);

const s3Client = new S3Client({});
const response = await s3Client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
return JSON.parse(await response.Body!.transformToString()) as CFNTemplate;
}
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,7 @@ __metadata:
amplify-nodejs-function-runtime-provider: 2.5.31
amplify-python-function-runtime-provider: 2.4.53
aws-cdk-lib: ~2.189.1
cdk-from-cfn: 0.258.0
chalk: ^4.1.1
ci-info: ^3.8.0
cli-table3: ^0.6.0
Expand Down Expand Up @@ -18957,6 +18958,13 @@ __metadata:
languageName: node
linkType: hard

"cdk-from-cfn@npm:0.258.0":
version: 0.258.0
resolution: "cdk-from-cfn@npm:0.258.0"
checksum: 20f61c4518ccedd7a651b9564b57ab527fb15851218367ae71d96492367540e9de858ef7e71a6bcee62ea392292085109579174b390b624af1983a407838aee1
languageName: node
linkType: hard

"chai@npm:^5.2.0":
version: 5.2.1
resolution: "chai@npm:5.2.1"
Expand Down
Loading