Skip to content

Commit

Permalink
Merge pull request aws-amplify#1698 from sundersc/rds-merge-to-main
Browse files Browse the repository at this point in the history
  • Loading branch information
sundersc authored Jul 21, 2023
2 parents 45946c3 + ef79245 commit 8ca0aba
Show file tree
Hide file tree
Showing 92 changed files with 2,800 additions and 1,212 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ module.exports = {
// Ignore CHANGELOG.md files
'/packages/*/CHANGELOG.md',

'/packages/amplify-graphql-model-transformer/rds-lambda',
'/packages/amplify-graphql-model-transformer/rds-patching-lambda',
'/packages/amplify-graphql-model-transformer/publish-notification-lambda',
'client-test-apps',
],
};
5 changes: 4 additions & 1 deletion packages/amplify-category-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@
},
"dependencies": {
"@aws-amplify/graphql-auth-transformer": "2.1.12",
"@aws-amplify/graphql-schema-generator": "0.1.8",
"@aws-amplify/graphql-schema-generator": "0.2.0",
"@aws-amplify/graphql-transformer": "0.0.7",
"@aws-amplify/graphql-transformer-core": "1.3.8",
"@aws-amplify/graphql-transformer-interfaces": "2.2.5",
"@aws-amplify/graphql-transformer-migrator": "2.1.12",
"@aws-cdk/aws-apigatewayv2-alpha": "~2.80.0-alpha.0",
"@aws-sdk/client-iam": "3.338.0",
"@aws-sdk/client-lambda": "3.338.0",
"@graphql-tools/merge": "^6.0.18",
"@octokit/rest": "^18.0.9",
"aws-sdk": "^2.1113.0",
Expand All @@ -60,6 +62,7 @@
"inquirer": "^7.3.3",
"js-yaml": "^4.0.0",
"lodash": "^4.17.21",
"node-fetch": "^2.6.7",
"ora": "^4.0.3",
"rimraf": "^3.0.0",
"uuid": "^8.3.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { parseDatabaseUrl } from '../../../../provider-utils/awscloudformation/utils/database-url';

describe('parseDatabaseUrl', () => {
it('should parse a valid database url', () => {
const databaseUrl = 'mysql://username:password@localhost:3306/database';
const expected = {
engine: 'mysql',
username: 'username',
password: 'password',
database: 'database',
host: 'localhost',
port: 3306,
};
const actual = parseDatabaseUrl(databaseUrl);
expect(actual).toEqual(expected);
});

it('should parse a valid database url without port', () => {
const databaseUrl = 'mysql://username:password@localhost/database';
const expected = {
engine: 'mysql',
username: 'username',
password: 'password',
database: 'database',
host: 'localhost',
port: NaN,
};
const actual = parseDatabaseUrl(databaseUrl);
expect(actual).toEqual(expected);
});

it('should parse a valid database url without username and password', () => {
const databaseUrl = 'mysql://localhost:3306/database';
const expected = {
engine: 'mysql',
username: '',
password: '',
database: 'database',
host: 'localhost',
port: 3306,
};
const actual = parseDatabaseUrl(databaseUrl);
expect(actual).toEqual(expected);
});

it('should throw an error for an invalid database url', () => {
const databaseUrl = 'http://username:password@localhost:3306/database';
expect(() => parseDatabaseUrl(databaseUrl)).toThrow('Invalid engine http.');
});

it('should accept uppercase engine name', () => {
const databaseUrl = 'MySQL://username:password@localhost:3306/database';
const expected = {
engine: 'mysql',
username: 'username',
password: 'password',
database: 'database',
host: 'localhost',
port: 3306,
};
const actual = parseDatabaseUrl(databaseUrl);
expect(actual).toEqual(expected);
});

it('should return empty object for an invalid database url', () => {
const databaseUrl = '1234567890';
const expected = {};
const actual = parseDatabaseUrl(databaseUrl);
expect(actual).toEqual(expected);
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Amplify Input read/write from schema constructs the global Amplify input from given config 1`] = `
"input Amplify {
"input AMPLIFY {
engine: String = \\"mysql\\"
globalAuthRule: AuthRule = {allow: public}
}"
Expand Down Expand Up @@ -129,7 +129,7 @@ Object {
"end": 13,
"start": 6,
},
"value": "Amplify",
"value": "AMPLIFY",
},
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('Amplify Input read/write from schema', () => {
});

it('constructs valid default input parameters for MySQL datasource with global auth rule', async () => {
const expectedGraphQLInputString = `input Amplify {
const expectedGraphQLInputString = `input AMPLIFY {
engine: String = \"mysql\"
globalAuthRule: AuthRule = { allow: public } # This "input" configures a global authorization rule to enable public access to all models in this schema. Learn more about authorization rules here:https://docs.amplify.aws/cli/graphql/authorization-rules
}`;
Expand All @@ -48,7 +48,7 @@ describe('Amplify Input read/write from schema', () => {
database: 'mockdatabase',
};

const mockInputSchema = `input Amplify {
const mockInputSchema = `input AMPLIFY {
engine: String = \"${mockValidInputs.engine}\"
globalAuthRule: AuthRule = { allow: public } # This "input" configures a global authorization rule to enable public access to all models in this schema. Learn more about authorization rules here:https://docs.amplify.aws/cli/graphql/authorization-rules
}`;
Expand All @@ -68,7 +68,7 @@ describe('Amplify Input read/write from schema', () => {
database: 'mockdatabase',
};

const mockInputSchema = `input Amplify {
const mockInputSchema = `input AMPLIFY {
engine: String = \"${mockValidInputs.engine}\"
globalAuthRule: AuthRule = { allow: public } # This "input" configures a global authorization rule to enable public access to all models in this schema. Learn more about authorization rules here:https://docs.amplify.aws/cli/graphql/authorization-rules
}`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { $TSContext, stateManager } from '@aws-amplify/amplify-cli-core';
import { getParameterStoreSecretPath } from '@aws-amplify/graphql-transformer-core';
import { getExistingConnectionSecrets } from '../../../../../provider-utils/awscloudformation/utils/rds-secrets/database-secrets';
import { getExistingConnectionSecrets } from '../../../../../provider-utils/awscloudformation/utils/rds-resources/database-resources';

const mockDatabase = 'mockdatabase';
const mockAPIName = 'mockapi';

jest.mock('../../../../../provider-utils/awscloudformation/utils/rds-secrets/ssmClient', () => ({
jest.mock('../../../../../provider-utils/awscloudformation/utils/rds-resources/ssmClient', () => ({
SSMClient: {
getInstance: jest.fn().mockResolvedValue({
getSecrets: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { $TSContext } from '@aws-amplify/amplify-cli-core';
import { SSMClient } from '../../../../../provider-utils/awscloudformation/utils/rds-resources/ssmClient';
import aws from 'aws-sdk';
import { SSMClient } from '../../../../../provider-utils/awscloudformation/utils/rds-secrets/ssmClient';

const secretName = 'mock-test-secret-name';
const secretValue = 'mock-test-secret-value';
Expand All @@ -20,6 +20,7 @@ const mockGetParameters = jest.fn(({ Names }) => {
jest.mock('aws-sdk', () => {
return {
config: {
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
update() {
return {};
},
Expand Down
40 changes: 8 additions & 32 deletions packages/amplify-category-api/src/commands/api/generate-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,24 @@ import { $TSContext } from '@aws-amplify/amplify-cli-core';
import { printer } from '@aws-amplify/amplify-prompts';
import fs from 'fs-extra';
import _ from 'lodash';
import {
ImportedRDSType,
RDS_SCHEMA_FILE_NAME,
ImportedDataSourceConfig,
RDSConnectionSecrets,
} from '@aws-amplify/graphql-transformer-core';
import { ImportedRDSType, RDS_SCHEMA_FILE_NAME, ImportedDataSourceConfig } from '@aws-amplify/graphql-transformer-core';
import { databaseConfigurationInputWalkthrough } from '../../provider-utils/awscloudformation/service-walkthroughs/import-appsync-api-walkthrough';
import { getAppSyncAPIName, getAPIResourceDir } from '../../provider-utils/awscloudformation/utils/amplify-meta-utils';
import {
getExistingConnectionSecrets,
storeConnectionSecrets,
getSecretsKey,
getDatabaseName,
} from '../../provider-utils/awscloudformation/utils/rds-secrets/database-secrets';
getConnectionSecrets,
} from '../../provider-utils/awscloudformation/utils/rds-resources/database-resources';
import { writeSchemaFile, generateRDSSchema } from '../../provider-utils/awscloudformation/utils/graphql-schema-utils';
import { PREVIEW_BANNER } from '../../category-constants';

const subcommand = 'generate-schema';

export const name = subcommand;

export const run = async (context: $TSContext) => {
printer.warn(PREVIEW_BANNER);
const apiName = getAppSyncAPIName();
const apiResourceDir = getAPIResourceDir(apiName);

Expand All @@ -36,7 +33,7 @@ export const run = async (context: $TSContext) => {
}

const engine = ImportedRDSType.MYSQL;
const secretsKey = await getSecretsKey();
const secretsKey = getSecretsKey();
const database = await getDatabaseName(context, apiName, secretsKey);
if (!database) {
printer.error(
Expand All @@ -46,10 +43,10 @@ export const run = async (context: $TSContext) => {
}

// read and validate the RDS connection secrets
const { secrets, storeSecrets } = await getConnectionSecrets(context, apiName, secretsKey, engine);
const { secrets, storeSecrets } = await getConnectionSecrets(context, secretsKey, engine);
const databaseConfig: ImportedDataSourceConfig = {
...secrets,
engine: engine,
engine,
};

const schemaString = await generateRDSSchema(context, databaseConfig, pathToSchemaFile);
Expand All @@ -64,24 +61,3 @@ export const run = async (context: $TSContext) => {
}
printer.info(`Successfully imported the schema definition for ${databaseConfig.database} database into ${pathToSchemaFile}`);
};

const getConnectionSecrets = async (
context: $TSContext,
apiName: string,
secretsKey: string,
engine: ImportedRDSType,
): Promise<{ secrets: RDSConnectionSecrets; storeSecrets: boolean }> => {
const existingSecrets = await getExistingConnectionSecrets(context, secretsKey, apiName);
if (existingSecrets) {
return {
secrets: existingSecrets,
storeSecrets: false,
};
}

const databaseConfig: ImportedDataSourceConfig = await databaseConfigurationInputWalkthrough(engine);
return {
secrets: databaseConfig,
storeSecrets: true,
};
};
2 changes: 2 additions & 0 deletions packages/amplify-category-api/src/commands/api/import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import { RDS_SCHEMA_FILE_NAME } from '@aws-amplify/graphql-transformer-core';
import { importAppSyncAPIWalkthrough } from '../../provider-utils/awscloudformation/service-walkthroughs/import-appsync-api-walkthrough';
import { getAPIResourceDir } from '../../provider-utils/awscloudformation/utils/amplify-meta-utils';
import { writeSchemaFile, generateRDSSchema } from '../../provider-utils/awscloudformation/utils/graphql-schema-utils';
import { PREVIEW_BANNER } from '../../category-constants';

const subcommand = 'import';

export const name = subcommand;

export const run = async (context: $TSContext) => {
printer.warn(PREVIEW_BANNER);
const importAppSyncAPIWalkInputs = await importAppSyncAPIWalkthrough(context);

if (importAppSyncAPIWalkInputs?.dataSourceConfig) {
Expand Down
12 changes: 6 additions & 6 deletions packages/amplify-category-api/src/commands/api/update-secrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ import { databaseConfigurationInputWalkthrough } from '../../provider-utils/awsc
import { getAppSyncAPIName, getAPIResourceDir } from '../../provider-utils/awscloudformation/utils/amplify-meta-utils';
import {
storeConnectionSecrets,
testDatabaseConnection,
getSecretsKey,
getDatabaseName,
} from '../../provider-utils/awscloudformation/utils/rds-secrets/database-secrets';
} from '../../provider-utils/awscloudformation/utils/rds-resources/database-resources';
import { PREVIEW_BANNER } from '../../category-constants';

const subcommand = 'update-secrets';

export const name = subcommand;

export const run = async (context: $TSContext) => {
printer.warn(PREVIEW_BANNER);

const apiName = getAppSyncAPIName();
const apiResourceDir = getAPIResourceDir(apiName);

Expand All @@ -30,13 +32,11 @@ export const run = async (context: $TSContext) => {

const engine = ImportedRDSType.MYSQL;
const secretsKey = await getSecretsKey();
const database = await getDatabaseName(context, apiName, secretsKey);

// read and validate the RDS connection parameters
const databaseConfig: ImportedDataSourceConfig = await databaseConfigurationInputWalkthrough(engine, database);
const databaseConfig: ImportedDataSourceConfig = await databaseConfigurationInputWalkthrough(engine);

await testDatabaseConnection(databaseConfig);
await storeConnectionSecrets(context, databaseConfig, apiName, secretsKey);

printer.info(`Successfully updated the secrets for ${database} database.`);
printer.info('Successfully updated the secrets for the database.');
};
Loading

0 comments on commit 8ca0aba

Please sign in to comment.