Skip to content

Commit

Permalink
fix: amplify meta output for imported ddb, type fixes (aws-amplify#8767)
Browse files Browse the repository at this point in the history
  • Loading branch information
jhockett authored and Sachin Panemangalore committed Nov 10, 2021
1 parent ae29a39 commit be7edde
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 58 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
import { $TSContext, ServiceSelection, stateManager } from 'amplify-cli-core';
import { $TSAny, $TSContext, $TSObject, ServiceSelection, stateManager } from 'amplify-cli-core';
import { printer } from 'amplify-prompts';
import { IDynamoDBService } from 'amplify-util-import';
import Enquirer from 'enquirer';
import _ from 'lodash';
import { importMessages } from './messages';
import {
ImportDynamoDBHeadlessParameters,
ProviderUtils,
DynamoDBBackendConfiguration,
DynamoDBEnvSpecificResourceParameters,
DynamoDBImportAnswers,
DynamoDBImportParameters,
DynamoDBMetaConfiguration,
DynamoDBMetaOutput,
DynamoDBResourceParameters,
ImportDynamoDBHeadlessParameters,
ProviderUtils,
} from './types';
import { IDynamoDBService } from 'amplify-util-import';

const attrReverseMap: $TSObject = {
S: 'string',
N: 'number',
B: 'binary',
BOOL: 'boolean',
L: 'list',
M: 'map',
NULL: null,
SS: 'string-set',
NS: 'number-set',
BS: 'binary-set',
};

export const importDynamoDB = async (
context: $TSContext,
Expand All @@ -23,7 +37,7 @@ export const importDynamoDB = async (
printSuccessMessage: boolean = true,
): Promise<{ envSpecificParameters: DynamoDBEnvSpecificResourceParameters } | undefined> => {
// Load provider
const providerPlugin = providerPluginInstance || require(serviceSelection.provider);
const providerPlugin = providerPluginInstance || (await import(serviceSelection.provider));
const providerUtils = providerPlugin as ProviderUtils;

const importServiceWalkthroughResult = await importServiceWalkthrough(
Expand All @@ -45,20 +59,20 @@ export const importDynamoDB = async (
const { envSpecificParameters } = await updateStateFiles(context, questionParameters, answers, persistEnvParameters);

if (printSuccessMessage) {
printSuccess(context, answers.tableName!);
printSuccess(answers.tableName!);
}

return {
envSpecificParameters,
};
};

const printSuccess = (context: $TSContext, tableName: string) => {
context.print.info('');
context.print.info(`✅ DynamoDB Table '${tableName}' was successfully imported.`);
context.print.info('');
context.print.info('Next steps:');
context.print.info(`- This resource can now be accessed from REST APIs (‘amplify add api’) and Functions (‘amplify add function’)`);
const printSuccess = (tableName: string) => {
printer.blankLine();
printer.info(`✅ DynamoDB Table '${tableName}' was successfully imported.`);
printer.blankLine();
printer.info('Next steps:');
printer.info(`- This resource can now be accessed from REST APIs (‘amplify add api’) and Functions (‘amplify add function’)`);
};

const importServiceWalkthrough = async (
Expand All @@ -82,7 +96,7 @@ const importServiceWalkthrough = async (

// Return it no userpools found in the project's region
if (_.isEmpty(tableList)) {
context.print.info(importMessages.NoDynamoDBTablesToImport);
printer.info(importMessages.NoDynamoDBTablesToImport);
return;
}

Expand All @@ -105,7 +119,7 @@ const importServiceWalkthrough = async (
answers.resourceName = answers.tableName.replace(/[\W_]+/g, '');
answers.tableDescription = await dynamoDB.getTableDetails(answers.tableName);

context.print.info(importMessages.OneTable(answers.tableName));
printer.info(importMessages.OneTable(answers.tableName));
} else {
const tableNameQuestion = {
type: 'autocomplete',
Expand All @@ -117,7 +131,7 @@ const importServiceWalkthrough = async (
footer: importMessages.AutoCompleteFooter,
};

const { tableName } = await enquirer.prompt(tableNameQuestion as any); // any case needed because async validation TS definition is not up to date
const { tableName } = await enquirer.prompt(tableNameQuestion as $TSAny); // any case needed because async validation TS definition is not up to date

answers.tableName = tableName!;
answers.resourceName = answers.tableName!.replace(/[\W_]+/g, '');
Expand Down Expand Up @@ -204,7 +218,7 @@ const createMetaOutput = (answers: DynamoDBImportAnswers, questionParameters: Dy

if (attribute) {
output.PartitionKeyName = hashKey.AttributeName;
output.PartitionKeyType = attribute.AttributeType;
output.PartitionKeyType = attrReverseMap[attribute.AttributeType];
}
}

Expand All @@ -213,7 +227,7 @@ const createMetaOutput = (answers: DynamoDBImportAnswers, questionParameters: Dy

if (attribute) {
output.SortKeyName = sortKeys[0].AttributeName;
output.SortKeyType = attribute.AttributeType;
output.SortKeyType = attrReverseMap[attribute.AttributeType];
}
}

Expand All @@ -239,7 +253,7 @@ const createEnvSpecificResourceParameters = (

if (attribute) {
envSpecificResourceParameters.partitionKeyName = hashKey.AttributeName;
envSpecificResourceParameters.partitionKeyType = attribute.AttributeType;
envSpecificResourceParameters.partitionKeyType = attrReverseMap[attribute.AttributeType];
}
}

Expand All @@ -248,7 +262,7 @@ const createEnvSpecificResourceParameters = (

if (attribute) {
envSpecificResourceParameters.sortKeyName = sortKeys[0].AttributeName;
envSpecificResourceParameters.sortKeyType = attribute.AttributeType;
envSpecificResourceParameters.sortKeyType = attrReverseMap[attribute.AttributeType];
}
}

Expand Down Expand Up @@ -312,8 +326,8 @@ export const importedDynamoDBEnvInit = async (
message: importMessages.ImportPreviousTable(resourceName, sourceEnvParams.tableName, context.exeInfo.sourceEnvName),
footer: importMessages.ImportPreviousResourceFooter,
initial: true,
format: (e: any) => (e ? 'Yes' : 'No'),
} as any);
format: (e: $TSAny) => (e ? 'Yes' : 'No'),
} as $TSAny);

if (!importExisting) {
return {
Expand All @@ -335,7 +349,7 @@ export const importedDynamoDBEnvInit = async (

// If there are no current parameters a service walkthrough is required, it can happen when pulling to an empty directory.
if (!(currentEnvSpecificParameters.tableName && currentEnvSpecificParameters.region)) {
context.print.info(importMessages.ImportNewResourceRequired(resourceName));
printer.info(importMessages.ImportNewResourceRequired(resourceName));

return {
doServiceWalkthrough: true,
Expand All @@ -357,7 +371,7 @@ export const importedDynamoDBEnvInit = async (
const tableExists = await dynamoDB.tableExists(currentEnvSpecificParameters.tableName);

if (!tableExists) {
context.print.error(importMessages.TableNotFound(currentEnvSpecificParameters.tableName));
printer.error(importMessages.TableNotFound(currentEnvSpecificParameters.tableName));

return {
succeeded: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { DynamoDBCLIInputs, DynamoDBCLIInputsGSIType } from '../service-walkthrough-types/dynamoDB-user-input-types';
import { AmplifyCategories, AmplifySupportedService, CLIInputSchemaValidator, JSONUtilities, pathManager } from 'amplify-cli-core';
import {
$TSAny,
$TSObject,
AmplifyCategories,
AmplifySupportedService,
CLIInputSchemaValidator,
JSONUtilities,
pathManager,
} from 'amplify-cli-core';
import * as fs from 'fs-extra';
import * as path from 'path';
import { DynamoDBCLIInputs, DynamoDBCLIInputsGSIType } from '../service-walkthrough-types/dynamoDB-user-input-types';

/* Need to move this logic to a base class */

Expand All @@ -27,7 +35,7 @@ export class DynamoDBInputState {

// Read cliInputs file if exists
try {
cliInputs = JSONUtilities.readJson(this._cliInputsFilePath) as DynamoDBCLIInputs;
cliInputs = JSONUtilities.readJson<DynamoDBCLIInputs>(this._cliInputsFilePath)!;
} catch (e) {
throw new Error('cli-inputs.json file missing from the resource directory');
}
Expand All @@ -51,7 +59,7 @@ export class DynamoDBInputState {
public saveCliInputPayload(cliInputs: DynamoDBCLIInputs): void {
this.isCLIInputsValid(cliInputs);

fs.ensureDirSync(path.join(pathManager.getBackendDirPath(), this._category, this._resourceName));
fs.ensureDirSync(pathManager.getResourceDirectoryPath(undefined, this._category, this._resourceName));
try {
JSONUtilities.writeJson(this._cliInputsFilePath, cliInputs);
} catch (e) {
Expand All @@ -61,7 +69,7 @@ export class DynamoDBInputState {

public migrate() {
let cliInputs: DynamoDBCLIInputs;
const attrReverseMap: any = {
const attrReverseMap: $TSObject = {
S: 'string',
N: 'number',
B: 'binary',
Expand All @@ -81,9 +89,9 @@ export class DynamoDBInputState {
const oldCFNFilepath = path.join(backendDir, 'storage', this._resourceName, `${this._resourceName}-cloudformation-template.json`);
const oldStorageParamsFilepath = path.join(backendDir, 'storage', this._resourceName, `storage-params.json`);

const oldParameters: any = JSONUtilities.readJson(oldParametersFilepath, { throwIfNotExist: true });
const oldCFN: any = JSONUtilities.readJson(oldCFNFilepath, { throwIfNotExist: true });
const oldStorageParams: any = JSONUtilities.readJson(oldStorageParamsFilepath, { throwIfNotExist: false }) || {};
const oldParameters = JSONUtilities.readJson<$TSAny>(oldParametersFilepath, { throwIfNotExist: true });
const oldCFN = JSONUtilities.readJson<$TSAny>(oldCFNFilepath, { throwIfNotExist: true });
const oldStorageParams = JSONUtilities.readJson<$TSAny>(oldStorageParamsFilepath, { throwIfNotExist: false }) || {};

const partitionKey = {
fieldName: oldParameters.partitionKeyName,
Expand All @@ -105,10 +113,10 @@ export class DynamoDBInputState {
triggerFunctions = oldStorageParams.triggerFunctions;
}

const getType = (attrList: any, attrName: string) => {
const getType = (attrList: $TSAny, attrName: string) => {
let attrType;

attrList.forEach((attr: any) => {
attrList.forEach((attr: $TSAny) => {
if (attr.AttributeName === attrName) {
attrType = attrReverseMap[attr.AttributeType];
}
Expand All @@ -120,10 +128,10 @@ export class DynamoDBInputState {
let gsi: DynamoDBCLIInputsGSIType[] = [];

if (oldCFN?.Resources?.DynamoDBTable?.Properties?.GlobalSecondaryIndexes) {
oldCFN.Resources.DynamoDBTable.Properties.GlobalSecondaryIndexes.forEach((cfnGSIValue: any) => {
let gsiValue: any = {};
oldCFN.Resources.DynamoDBTable.Properties.GlobalSecondaryIndexes.forEach((cfnGSIValue: $TSAny) => {
let gsiValue: $TSAny = {};
(gsiValue.name = cfnGSIValue.IndexName),
cfnGSIValue.KeySchema.forEach((keySchema: any) => {
cfnGSIValue.KeySchema.forEach((keySchema: $TSObject) => {
if (keySchema.KeyType === 'HASH') {
gsiValue.partitionKey = {
fieldName: keySchema.AttributeName,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import * as path from 'path';
import {
$TSAny,
$TSContext,
$TSObject,
AmplifyCategories,
AmplifySupportedService,
exitOnNextTick,
pathManager,
ResourceDoesNotExistError,
stateManager,
} from 'amplify-cli-core';
import { alphanumeric, printer, prompter, Validator } from 'amplify-prompts';
import * as fs from 'fs-extra';
import * as path from 'path';
import { v4 as uuid } from 'uuid';
import { alphanumeric, printer, prompter, Validator } from 'amplify-prompts';
import { $TSContext, AmplifyCategories, ResourceDoesNotExistError, exitOnNextTick, stateManager } from 'amplify-cli-core';
import { DynamoDBInputState } from './dynamoDB-input-state';
import { DDBStackTransform } from '../cdk-stack-builder/ddb-stack-transform';
import {
DynamoDBAttributeDefType,
DynamoDBCLIInputs,
DynamoDBCLIInputsGSIType,
DynamoDBCLIInputsKeyType,
} from '../service-walkthrough-types/dynamoDB-user-input-types';
import { DDBStackTransform } from '../cdk-stack-builder/ddb-stack-transform';
import { ConfigSnapshotDeliveryProperties } from 'cloudform-types/types/config/deliveryChannel';
import { DynamoDBInputState } from './dynamoDB-input-state';

// keep in sync with ServiceName in amplify-AmplifyCategories.STORAGE-function, but probably it will not change
const FunctionServiceNameLambdaFunction = 'Lambda';
Expand Down Expand Up @@ -58,7 +67,7 @@ export async function addWalkthrough(context: $TSContext, defaultValuesFilename:

export async function updateWalkthrough(context: $TSContext) {
const amplifyMeta = stateManager.getMeta();
const dynamoDbResources: any = {};
const dynamoDbResources: $TSObject = {};

Object.keys(amplifyMeta[AmplifyCategories.STORAGE]).forEach(resourceName => {
if (
Expand Down Expand Up @@ -519,7 +528,7 @@ async function addTrigger(context: $TSContext, resourceName: string, triggerList
// Update amplify-meta and backend-config

const backendConfigs = {
service: FunctionServiceNameLambdaFunction,
service: AmplifySupportedService.LAMBDA,
providerPlugin: 'awscloudformation',
build: true,
};
Expand Down Expand Up @@ -599,7 +608,7 @@ async function addTrigger(context: $TSContext, resourceName: string, triggerList

// Update dependsOn

const amplifyMetaFilePath = context.amplify.pathManager.getAmplifyMetaFilePath();
const amplifyMetaFilePath = pathManager.getAmplifyMetaFilePath();
const amplifyMeta = context.amplify.readJsonFile(amplifyMetaFilePath);

const resourceDependsOn = amplifyMeta.function[functionName].dependsOn || [];
Expand Down Expand Up @@ -641,13 +650,13 @@ async function addTrigger(context: $TSContext, resourceName: string, triggerList
async function getLambdaFunctions(context: $TSContext) {
const { allResources } = await context.amplify.getResourceStatus();
const lambdaResources = allResources
.filter((resource: any) => resource.service === FunctionServiceNameLambdaFunction)
.filter((resource: any) => resource.service === AmplifySupportedService.LAMBDA)
.map((resource: any) => resource.resourceName);

return lambdaResources;
}

function migrateCategory(context: $TSContext, projectPath: any, resourceName: any) {
export function migrate(context: $TSContext, projectPath: any, resourceName: any) {
const resourceDirPath = path.join(projectPath, 'amplify', 'backend', AmplifyCategories.STORAGE, resourceName);
const cfnFilePath = path.join(resourceDirPath, `${resourceName}-cloudformation-template.json`);

Expand Down Expand Up @@ -722,11 +731,11 @@ function migrateCategory(context: $TSContext, projectPath: any, resourceName: an
fs.writeFileSync(cfnFilePath, jsonString, 'utf8');
}

function getIAMPolicies(resourceName: any, crudOptions: any) {
export function getIAMPolicies(resourceName: string, crudOptions: $TSAny) {
let policy = {};
const actions: any = [];
const actions: string[] = [];

crudOptions.forEach((crudOption: any) => {
crudOptions.forEach((crudOption: $TSAny) => {
switch (crudOption) {
case 'create':
actions.push('dynamodb:Put*', 'dynamodb:Create*', 'dynamodb:BatchWriteItem');
Expand Down Expand Up @@ -770,10 +779,3 @@ function getIAMPolicies(resourceName: any, crudOptions: any) {

return { policy, attributes };
}

module.exports = {
addWalkthrough,
updateWalkthrough,
migrate: migrateCategory,
getIAMPolicies,
};
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ describe('dynamodb import', () => {

teamInfo = getTeamProviderInfo(projectRoot);

// No prod in team proovider info
// No prod in team provider info
expect(teamInfo.prod).toBeUndefined();
});

Expand Down

0 comments on commit be7edde

Please sign in to comment.