Skip to content

Commit

Permalink
feat: ability to add custom EventSource and lambda triggers via ampli…
Browse files Browse the repository at this point in the history
…fy add function, kinesis support in analytics category (#2463)
  • Loading branch information
ambientlight committed Feb 25, 2020
1 parent 12345da commit b25cfd0
Show file tree
Hide file tree
Showing 36 changed files with 1,984 additions and 184 deletions.
4 changes: 4 additions & 0 deletions packages/amplify-category-analytics/amplify-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
"add",
"remove",
"push",
"update",
"console",
"help"
],
"commandAliases":{
"configure": "update"
},
"eventHandlers": []
}
4 changes: 4 additions & 0 deletions packages/amplify-category-analytics/commands/analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ module.exports = {
name: 'add',
description: `Takes you through a CLI flow to add an ${featureName} resource to your local backend`,
},
{
name: 'update',
description: `Takes you through steps in the CLI to update an ${featureName} resource`,
},
{
name: 'push',
description: `Provisions only ${featureName} cloud resources with the latest local developments`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = {
const { amplify } = context;
const servicesMetadata = amplify.readJsonFile(`${__dirname}/../../provider-utils/supported-services.json`);
return amplify
.serviceSelectionPrompt(context, category, servicesMetadata)
.serviceSelectionPrompt(context, category, servicesMetadata, 'Select an Analytics provider')
.then(result => {
options = {
service: result.service,
Expand Down
43 changes: 43 additions & 0 deletions packages/amplify-category-analytics/commands/analytics/update.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const subcommand = 'update';
const category = 'analytics';

module.exports = {
name: subcommand,
alias: ['configure'],
run: async context => {
const { amplify } = context;
const servicesMetadata = amplify.readJsonFile(`${__dirname}/../../provider-utils/supported-services.json`);

return amplify
.serviceSelectionPrompt(context, category, servicesMetadata)
.then(result => {
const options = {
service: result.service,
providerPlugin: result.providerName,
};

const providerController = require(`../../provider-utils/${result.providerName}/index`);
if (!providerController) {
context.print.error('Provider not configured for this category');
return;
}

return providerController.updateResource(context, category, result.service, options);
})
.then(resourceName => {
const { print } = context;
print.success(`Successfully updated resource ${resourceName} locally`);
print.info('');
print.success('Some next steps:');
print.info('"amplify push" will build all your local backend resources and provision it in the cloud');
print.info(
'"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud',
);
print.info('');
})
.catch(err => {
context.print.info(err.stack);
context.print.error(`There was an error updating the ${category} resource`);
});
},
};
40 changes: 37 additions & 3 deletions packages/amplify-category-analytics/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,45 @@
const path = require('path');
const inquirer = require('inquirer');
const pinpointHelper = require('./lib/pinpoint-helper');
const kinesisHelper = require('./lib/kinesis-helper');
const { migrate } = require('./provider-utils/awscloudformation/service-walkthroughs/pinpoint-walkthrough');

const category = 'analytics';

function console(context) {
pinpointHelper.console(context);
async function console(context) {
const hasKinesisResource = kinesisHelper.hasResource(context);
const hasPinpointResource = pinpointHelper.hasResource(context);

let selectedResource;
if (hasKinesisResource && hasPinpointResource) {
const question = {
name: 'resource',
message: 'Select resource',
type: 'list',
choices: ['kinesis', 'pinpoint'],
required: true,
};

const result = await inquirer.prompt(question);
selectedResource = result.resource;
} else if (hasKinesisResource) {
selectedResource = 'kinesis';
} else if (hasPinpointResource) {
selectedResource = 'pinpoint';
} else {
context.print.error('Neither analytics nor notifications is enabled in the cloud.');
}

switch (selectedResource) {
case 'kinesis':
kinesisHelper.console(context);
break;
case 'pinpoint':
pinpointHelper.console(context);
break;
default:
break;
}
}

async function getPermissionPolicies(context, resourceOpsMapping) {
Expand All @@ -22,7 +56,7 @@ async function getPermissionPolicies(context, resourceOpsMapping) {
context,
amplifyMeta[category][resourceName].service,
resourceName,
resourceOpsMapping[resourceName]
resourceOpsMapping[resourceName],
);
permissionPolicies.push(policy);
resourceAttributes.push({ resourceName, attributes, category });
Expand Down
1 change: 1 addition & 0 deletions packages/amplify-category-analytics/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ module.exports = {
CategoryName: 'analytics',
NotificationsCategoryName: 'notifications',
PinpointName: 'Pinpoint',
KinesisName: 'Kinesis',
};
54 changes: 54 additions & 0 deletions packages/amplify-category-analytics/lib/kinesis-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const open = require('open');
const constants = require('./constants');

function console(context) {
const amplifyMeta = context.amplify.getProjectMeta();
const { envName } = context.amplify.getEnvInfo();
const region = context.amplify.getEnvDetails()[envName].awscloudformation.Region;

const kinesisApp = scanCategoryMetaForKinesis(amplifyMeta[constants.CategoryName]);
if (kinesisApp) {
const { Id } = kinesisApp;
const consoleUrl = `https://${region}.console.aws.amazon.com/kinesis/home?region=${region}#/streams/details?streamName=${Id}&tab=details`;
open(consoleUrl, { wait: false });
} else {
context.print.error('Kinesis is not enabled in the cloud.');
}
}

function scanCategoryMetaForKinesis(categoryMeta) {
// single kinesis resource for now
let result;
if (categoryMeta) {
const services = Object.keys(categoryMeta);
for (let i = 0; i < services.length; i++) {
const serviceMeta = categoryMeta[services[i]];
if (serviceMeta.service === constants.KinesisName && serviceMeta.output && serviceMeta.output.kinesisStreamId) {
result = {
Id: serviceMeta.output.kinesisStreamId,
};
if (serviceMeta.output.Name) {
result.Name = serviceMeta.output.Name;
} else if (serviceMeta.output.appName) {
result.Name = serviceMeta.output.appName;
}

if (serviceMeta.output.Region) {
result.Region = serviceMeta.output.Region;
}
break;
}
}
}
return result;
}

function hasResource(context) {
const amplifyMeta = context.amplify.getProjectMeta();
return scanCategoryMetaForKinesis(amplifyMeta[constants.CategoryName]) !== undefined;
}

module.exports = {
console,
hasResource,
};
11 changes: 11 additions & 0 deletions packages/amplify-category-analytics/lib/pinpoint-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ function scanCategoryMetaForPinpoint(categoryMeta) {
return result;
}

function hasResource(context) {
const amplifyMeta = context.amplify.getProjectMeta();
let pinpointApp = scanCategoryMetaForPinpoint(amplifyMeta[constants.CategoryName]);
if (!pinpointApp) {
pinpointApp = scanCategoryMetaForPinpoint(amplifyMeta[constants.NotificationsCategoryName]);
}

return pinpointApp !== undefined;
}

module.exports = {
hasResource,
console,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Pinpoint resource stack creation using Amplify CLI",
"Parameters": {
"env": {
"Type": "String"
},
"kinesisStreamName": {
"Type": "String"
},
"kinesisStreamShardCount": {
"Type": "Number",
"Default": 1
},
"authPolicyName": {
"Type": "String"
},
"unauthPolicyName": {
"Type": "String"
},
"authRoleName": {
"Type": "String"
},
"unauthRoleName": {
"Type": "String"
}
},
"Conditions": {
"ShouldNotCreateEnvResources": {
"Fn::Equals": [
{ "Ref": "env" },
"NONE"
]
}
},
"Resources": {
"KinesisStream": {
"Type": "AWS::Kinesis::Stream",
"Properties": {
"Name": {
"Fn::Join": [
"-",
[
{ "Ref": "kinesisStreamName" },
{ "Ref": "env"}
]
]
},
"ShardCount": { "Ref": "kinesisStreamShardCount" }
}
},
"CognitoUnauthPolicy": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": {"Ref": "unauthPolicyName" },
"Roles": [
{"Ref": "unauthRoleName" }
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"kinesis:PutRecord",
"kinesis:PutRecords"
],
"Resource": {
"Fn::GetAtt": ["KinesisStream", "Arn"]
}
}]
}
}
},
"CognitoAuthPolicy": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": {"Ref": "authPolicyName" },
"Roles": [
{"Ref": "authRoleName" }
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"kinesis:PutRecord",
"kinesis:PutRecords"
],
"Resource": {
"Fn::GetAtt": ["KinesisStream", "Arn"]
}
}]
}
}
}
},
"Outputs": {
"kinesisStreamArn": {
"Value": {"Fn::GetAtt": ["KinesisStream", "Arn"] }
},
"kinesisStreamId": {
"Value": {"Ref": "KinesisStream" }
},
"kinesisStreamShardCount": {
"Value": {"Ref": "kinesisStreamShardCount" }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const uuid = require('uuid');

const getAllDefaults = project => {
const appName = project.projectConfig.projectName.toLowerCase();
const [shortId] = uuid().split('-');

const authRoleName = {
Ref: 'AuthRoleName',
};

const unauthRoleName = {
Ref: 'UnauthRoleName',
};

const defaults = {
kinesisStreamName: `${appName}Kinesis`,
kinesisStreamShardCount: 1,
authRoleName,
unauthRoleName,
authPolicyName: `kinesis_amplify_${shortId}`,
unauthPolicyName: `kinesis_amplify_${shortId}`,
};

return defaults;
};

module.exports = {
getAllDefaults,
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ function addResource(context, category, service) {
return addWalkthrough(context, defaultValuesFilename, serviceMetadata);
}

function updateResource(context, category, service) {
const serviceMetadata = context.amplify.readJsonFile(`${__dirname}/../supported-services.json`)[service];
const { defaultValuesFilename, serviceWalkthroughFilename } = serviceMetadata;
const serviceWalkthroughSrc = `${__dirname}/service-walkthroughs/${serviceWalkthroughFilename}`;
const { updateWalkthrough } = require(serviceWalkthroughSrc);

if (!updateWalkthrough) {
context.print.error('Update functionality not available for this service');
process.exit(0);
}

return updateWalkthrough(context, defaultValuesFilename, serviceMetadata);
}

function getPermissionPolicies(context, service, resourceName, crudOptions) {
const serviceMetadata = context.amplify.readJsonFile(`${__dirname}/../supported-services.json`)[service];
const { serviceWalkthroughFilename } = serviceMetadata;
Expand All @@ -22,4 +36,4 @@ function getPermissionPolicies(context, service, resourceName, crudOptions) {
return getIAMPolicies(resourceName, crudOptions);
}

module.exports = { addResource, getPermissionPolicies };
module.exports = { addResource, getPermissionPolicies, updateResource };
Loading

0 comments on commit b25cfd0

Please sign in to comment.