├── .eslintrc.json ├── .gitignore ├── LICENSE ├── README.md ├── bin └── cli.js ├── methods ├── .DS_Store ├── AWS │ ├── deleteGatewayv2.js │ ├── gatewayv2.js │ ├── getGatewayv2.js │ ├── iam.js │ ├── lambda.js │ └── s3.js ├── commands │ ├── aliases.js │ ├── apis.js │ ├── buckets.js │ ├── functions.js │ ├── layers.js │ └── roles.js └── util │ ├── archiver.js │ ├── aws.js │ ├── chalkColors.js │ ├── default.js │ ├── generateEnv.js │ └── verifyAWS.js ├── npm_package └── index.js ├── package-lock.json └── package.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "es6": true 6 | }, 7 | "plugins": ["react"], 8 | "extends": ["eslint:recommended", "plugin:react/recommended"], 9 | "parserOptions": { 10 | "ecmaVersion": 8, 11 | "sourceType": "module", 12 | "ecmaFeatures": { 13 | "impliedStrict": true, 14 | "jsx": true 15 | } 16 | }, 17 | "rules": { 18 | "indent": ["warn", 2], 19 | "no-unused-vars": ["off", { "vars": "local" }], 20 | "prefer-const": "warn", 21 | "quotes": ["warn", "single"], 22 | "react/prop-types": "off", 23 | "semi": ["warn", "always"], 24 | "space-infix-ops": "warn", 25 | "linebreak-style": ["warn", "unix"] 26 | }, 27 | "settings": { 28 | "react": { "version": "detect"} 29 | } 30 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .env 3 | .vscode/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 alanajs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # alanajs 2 | 3 | 4 |
5 | 6 | 7 |
8 |

AWS Lambda and API Gateway, simplified for JavaScript

9 |
10 | 11 | 12 | 13 | [![Issues][issues-shield]][issues-url] 14 | [![MIT License][license-shield]][license-url] 15 | 16 |
17 | 18 | 19 | ## About alanajs 20 | 21 | Make setting up Lambda microservices easier than ever. alanajs is a free, open-source npm package that consolidates functionality provided by AWS SDK and AWS CLI, automating deployment and configuration tasks by making intelligent assumptions about the deployment sequence according to best practices. alanajs makes it easy to deploy Lambda functions with dependencies and layers, and it also simplifies creating routes, APIs, and integrations with Lambda on AWS API Gateway. 22 | 23 | Here is a [Medium](https://medium.com/@jaehyunha/introducing-alanajs-aws-lambda-and-api-gateway-simplified-for-javascript-5108cb8773eb) article describing the story behind alanajs. 24 | 25 | You can also visit us here. 26 | 27 | ## Table of Contents 28 | 29 |
30 | Table of Contents 31 |
    32 |
  1. About alanajs
  2. 33 |
  3. Getting Started
  4. 34 | 37 |
  5. Enhancement and Improvements
  6. 38 | 41 |
  7. Reporting Issues
  8. 42 |
  9. Contributors
  10. 43 |
  11. License
  12. 44 |
45 |
46 | 47 |

(back to top)

48 | 49 | ## Getting Started 50 | 51 | This section describes the instructions for end users who would like to download the package and connect their AWS account. For developers who would like to contribute to the open-source project, follow these [instructions](#enhancement-and-improvements). 52 | 53 | ### Installation and Setup 54 | 55 | 1. Install alanajs as a package dependency. 56 | 57 | ```sh 58 | npm install alanajs 59 | ``` 60 | 61 | 2. Update the `.env` file in the project root directory with the necessary credentials OR see Step 3 to init through the command line. 62 | 63 | ```sh 64 | AWS_ACCESS_KEY_ID= 65 | AWS_SECRET_ACCESS_KEY= 66 | AWS_REGION= 67 | ROLENAME= 68 | S3BUCKETNAME= 69 | AWS_ACCOUNT= 70 | FOLDER= 71 | ``` 72 | 73 | 3. Run the follow through the command line to update .env file or create one if it does not exist. Replace the parameters with user's details. Refer to documentation for more details. The DIRECTORY is the main folder to store files, dependencies, and directories as Lambda functions and layers. 74 | 75 | ```sh 76 | alana init [AWS_ACCOUNT] [AWS_REGION] -r [ROLENAME] -b [S3BUCKETNAME] -d [DIRECTORY] 77 | ``` 78 | 79 | 4. Import the package to start using alana methods. 80 | 81 | ```sh 82 | import 'alana' from 'alanajs'; 83 | ``` 84 | 85 | 5. That's it! You are ready to start running code through the command line or by running node [fileName] to execute the functions. 86 | 87 |

(back to top)

88 |
89 | 90 |
91 |

Figure 1: Create and Update Lambda Function

92 |
93 |
94 | 95 |
96 |

Figure 2: Create and Update Lambda Layers

97 | 98 |
99 |
100 | 101 |
102 |

Figure 3: Create, Update and Delete Alias

103 | 104 |
105 |
106 | 107 |
108 |

Figure 4: Create, Update and Delete API Gateway

109 | 110 |
111 |
112 | 113 |
114 |

Figure 5: Create, Update and Delete Routes

115 | 116 |
117 | 118 |

(back to top)

119 | 120 | ## Enhancement and Improvements 121 | 122 | This section describes the instructions for developers who would like to contribute to the open-source project. For users who would like to download the package and connect their AWS account, follow these [instructions](#getting-started) instead. 123 | 124 | ### Built With 125 | 126 | The alanajs application was built using the following: 127 | 128 | - Node 129 | - Commander 130 | - AWS SDK for Javascript V3 131 | 132 | 1. Fork the project. 133 | 134 | 2. Create a feature branch. 135 | 136 | ```sh 137 | git checkout -b feature/featureName 138 | ``` 139 | 140 | 3. Install package dependencies. 141 | 142 | ```sh 143 | npm install 144 | ``` 145 | 146 | 4. Update the `.env` file in the project root directory with the necessary credentials OR init through the command line. 147 | 148 | ```sh 149 | AWS_ACCESS_KEY_ID= 150 | AWS_SECRET_ACCESS_KEY= 151 | AWS_REGION= 152 | ROLENAME= 153 | S3BUCKETNAME= 154 | AWS_ACCOUNT= 155 | FOLDER= 156 | ``` 157 | 158 | ```sh 159 | alana init [AWS_ACCOUNT] [AWS_REGION] -r [ROLENAME] -b [S3BUCKETNAME] -d [DIRECTORY] 160 | ``` 161 | 162 | 5. Add and commit your changes. 163 | 164 | ```sh 165 | git add ... 166 | git commit -m 'Add some feature functionality' 167 | ``` 168 | 169 | 6. Push to the branch. 170 | 171 | ```sh 172 | git push origin feature/featureName 173 | ``` 174 | 175 | 7. Open a Pull Request here. 176 | 177 |

(back to top)

178 | 179 | 180 | ## Reporting Issues 181 | 182 | Bugs are tracked through GitHub issues. Create an issue on our repository and provide the following information based on this template: 183 | 184 | - **Descriptive title**: Provide a descriptive title for your issue. 185 | - **Describe the issue**: Describe the steps you took leading up to the issue. Try to provide code or screenshots. 186 | - **Expected behavior**: Describe the expected behavior. 187 | 188 | 189 |

(back to top)

190 | 191 | 192 | ## Contributors 193 | 194 | - Tin Khin - 195 | Github | 196 | Linkedin 197 | - Eugene Lee - 198 | Github | 199 | Linkedin 200 | 201 | - Amy Liang - 202 | Github | 203 | Linkedin 204 | 205 | - Jae Hyun Ha - 206 | Github | 207 | Linkedin 208 | 209 | 210 | Project Links: Github | Linkedin | Medium | Visit Us 211 | 212 |

(back to top)

213 | 214 | 215 | ## License 216 | 217 | Distributed under the MIT License. See the [LICENSE](https://github.com/oslabs-beta/alanajs/blob/master/LICENSE) for details 218 | 219 | 220 | 221 | 222 | [contributors-shield]: https://img.shields.io/github/contributors/oslabs-beta/alanajs.svg?style=for-the-badge 223 | [contributors-url]: https://github.com/oslabs-beta/alanajs/graphs/contributors 224 | [stars-shield]: https://img.shields.io/github/stars/oslabs-beta/alanajs.svg?style=for-the-badge 225 | [stars-url]: https://github.com/oslabs-beta/alanajs/stargazers 226 | [issues-shield]: https://img.shields.io/github/issues/oslabs-beta/alanajs.svg?style=for-the-badge 227 | [issues-url]: https://github.com/oslabs-beta/alanajs/issues 228 | [license-shield]: https://img.shields.io/github/license/oslabs-beta/alanajs.svg?style=for-the-badge 229 | [license-url]: https://github.com/oslabs-beta/alanajs/blob/master/LICENSE 230 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 231 | [linkedin-url]: https://www.linkedin.com/company/alanajs 232 | [product-screenshot]: client/src/Dashboard/assets/img/helios-blue-logo-t.png 233 | -------------------------------------------------------------------------------- /bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // npm packages 3 | import { program, Command } from 'commander'; 4 | import dotenv from 'dotenv'; 5 | 6 | // commands 7 | import lambdaFunctions from '../methods/commands/functions.js'; 8 | import layers from '../methods/commands/layers.js'; 9 | import aliases from '../methods/commands/aliases.js'; 10 | import roles from '../methods/commands/roles.js'; 11 | import buckets from '../methods/commands/buckets.js'; 12 | import apis from '../methods/commands/apis.js'; 13 | 14 | // init 15 | import init from '../methods/util/generateEnv.js'; 16 | 17 | // consts 18 | import {AwsBucket, AwsRegion, AwsRole } from '../methods/util/aws.js'; 19 | import {startingBucket, startingRegion, startingRole, startingFolder} from '../methods/util/default.js'; 20 | import { intro, starting, error, fail, finished, code } from '../methods/util/chalkColors.js'; 21 | 22 | dotenv.config(); 23 | 24 | const envFolder = process.env.FOLDER; 25 | 26 | // local variables 27 | const hasCredentials = !!(process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY && process.env.AWS_REGION); 28 | const defaultBucket = AwsBucket || startingBucket; 29 | const defaultRegion = AwsRegion || startingRegion; 30 | const defaultRole = AwsRole || startingRole; 31 | const defaultFolder = envFolder || startingFolder; 32 | 33 | console.clear(); 34 | 35 | if (!hasCredentials) { 36 | program 37 | . addHelpText('before', intro('Welcome to alanajs.') 38 | + '\nNo AWS credentials were found, so start by issuing the following command:\n\n ' 39 | + code('alana init ') 40 | + '\n\nThere are additional options as well. Once the AWS credentials are entered, more options are available to you.' 41 | + '\nCheck out help by attaching the -h or --help flag to the command line' 42 | + '\n see below for the command line usage\n'); 43 | } 44 | 45 | program 46 | .command('init') 47 | . addHelpText('before', intro('alanajs\n') 48 | + 'This will initialize or update the .ENV file needed to run alanajs. Admin privileges are required to verify the account number\n' 49 | + 'as well as read/write privileges are needed to interact with S3, Lambda, and API Gateway. If the user ID and Key do not have these\n' 50 | + 'permissions, there may be unexpected errors when trying to create, invoke, or otherwise interact with AWS. Please ensure the ID and\n' 51 | + 'and Key are safely secured otherwise.\n' 52 | + 'Usage Examples:\n\n ' 53 | + code('alana init id111 key2222 account3 ') + '- enters a AWS ID and Key which will verify it against account3 \n ' 54 | + code('alana init id4 key5 -b bucket2 -u ') + '- updates the AWS ID and Key and sets the default bucket to bucket2\n ' 55 | + code('alana init id6 key7 -d Lambda -u ') + '- updates the AWS ID and Key and sets the default directory to to /Lambda\n ' 56 | + '\nSee below for the command line usage\n') 57 | .description('run this to configure access to AWS') 58 | .argument('', 'this is your AWS access key ID') 59 | .argument('', 'this is your AWS secret access key') 60 | .argument('[AWS_ACCOUNT]', 'this is your AWS account number') 61 | .argument('[region]', 'this is your preferred AWS region', defaultRegion) 62 | .option('-r, --role ', 'the AWS Role to be used', defaultRole) 63 | .option('-b, --bucket ', 'the name of the S3 bucket to be used', defaultBucket) 64 | .option('-u, --update', 'set this flag to override and update AWS credentials') 65 | .option('-d, --directory ', 'the directory that files to upload are located in', defaultFolder) 66 | .action(async (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_ACCOUNT, region, options) => { 67 | await init(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_ACCOUNT, region, options.role, options.bucket, options.directory, options.update); 68 | }); 69 | 70 | 71 | // only show these if the user has input some credentials 72 | if (hasCredentials) { 73 | program 74 | . addHelpText('before', intro('alanajs\n') 75 | + 'Usage Examples:\n\n ' 76 | + code('alana init ') + '- enters a new AWS ID and Key\n ' 77 | + code('alana list ') + '- gets a table of all current AWS Lambda functions\n ' 78 | + code('alana create hello hello.js file1.js file2.js ') + '- creates a Lambda function named hello where the definition is located in hello.js\n' 79 | + ' with dependencies in file1.js and file2.js\n ' 80 | + code('alana update foo codeV2.js ') + '- updates the Lambda function named foo with the code located in codeV2.js\n ' 81 | + code('alana delete foo 2 ') + '- delete the Lambda function foo if it has a version 2\n ' 82 | + '\nSee below for the command line usage\n'); 83 | 84 | program 85 | .command('create') 86 | .addHelpText('before', intro('alanajs\n') 87 | + 'This allows you to interact with Lambda functions, verifying that requirements exist and create them before attempting to create.\n' 88 | + 'Usage Examples:\n\n ' 89 | + code('alana create testFunction function.js ') + '- creates a new Lambda function named testFunction from the file function.js\n ' 90 | + code('alana create testFunction function.js -d "description" ') + '- creates a new Lambda function named testFunction from the file function.js and adds a description\n ' 91 | + code('alana create layer1 function.js ') + '- creates a new Lambda layer named layer1 from the file function.js\n ' 92 | + code('alana create -r testrole testfunction function.js ') + '- creates a new Lambda layer named layer1 from the file function.js and give it the permission of testrole\n ' 93 | + '\nSee below for the command line usage\n') 94 | .description('zip and create lambda function') 95 | .argument('[funcName]', 'the name of the Lambda function to be create. If not specified, will only verify and create requirements') 96 | .argument('[fileArr...]', 'the file array that needs to be inclided for the Lambda function') 97 | .option('-r, --role [role name]', 'specifying a different AWS role than default specifically for this function') 98 | .option('-b, --bucket ', 'specifying a different S3 bucket name than default') 99 | .option('-d, --description ', 'a description of what the function is supposed to do') 100 | .option('-p, --publish', 'publish a new version of the Lambda function') 101 | .option('-l, --layerName ', 'create AWS lambda layer') 102 | .action(async (funcName, fileArr, options) => { 103 | 104 | // do not create a function if the options don't exist 105 | if (Object.keys(options).length === 0){ 106 | if (funcName && fileArr.length === 0) { 107 | console.log(error('File name(s) are required if a function is to be created')); 108 | return; 109 | } 110 | if (!funcName && fileArr.length === 0) { 111 | console.log(error('Function name and file name(s) are required if a function is to be created')); 112 | return; 113 | } 114 | } 115 | 116 | lambdaFunctions.create(funcName, fileArr, options); 117 | 118 | }); 119 | 120 | program 121 | .command('list') 122 | .addHelpText('before', intro('alanajs\n') 123 | + 'This will get information about the Lambda functions by listing all or listing the versions\n' 124 | + 'Usage Examples:\n\n ' 125 | + code('alana list -F ') + '- lists all the Lambda functions\n ' 126 | + code('alana list -f test ') + '- lists all the versions that exist of function test\n ' 127 | + '\nSee below for the command line usage\n') 128 | .description('list the various items') 129 | .option('-F, --functions', 'list all the Lambda functions') 130 | .option('-f, --function ', 'list a specific function versions') 131 | .action(async (options) => { 132 | if (options.functions) await lambdaFunctions.list(options); 133 | }); 134 | 135 | program 136 | .command('delete') 137 | .addHelpText('before', intro('alanajs\n') 138 | + 'This will allow you to delete a specific Lambda function or a version\n' 139 | + 'Usage Examples:\n\n ' 140 | + code('alana delete foo ') + '- deletes the function foo\n ' 141 | + code('alana delete bar 5 ') + '- deletes the function bar version 5\n ' 142 | + '\nSee below for the command line usage\n') 143 | .description('delete a lambda function') 144 | .argument('') 145 | .argument('[qualifier]') 146 | .action( (funcName, qualifier) => { 147 | lambdaFunctions.delete(funcName, qualifier); 148 | }); 149 | 150 | program 151 | .command('update') 152 | .addHelpText('before', intro('alanajs\n') 153 | + 'This will allow you to update a specific Lambda function or a version\n' 154 | + 'Usage Examples:\n\n ' 155 | + code('alana update foo bar.js ') + '- updates the function foo with the file bar.js\n ' 156 | + code('alana update foo bar.js -d "description" ') + '- updates the function foo with the file bar.js and description\n ' 157 | + code('alana update foo bar.js bar2.js folder/bar3.js ') + '- updates the function foo with the file bar.js and dependencies bar2.js and bar3.js\n ' 158 | + '\nSee below for the command line usage\n') 159 | .description('update a Lambda function') 160 | .argument('') 161 | .argument('') 162 | .option('-d, --description ', 'a description of what the function is supposed to do') 163 | .option('-p, --publish', 'publish a new version of the Lambda function') 164 | .description('zip and update lambda function') 165 | .action(async (funcName, fileArr, options) => { 166 | await lambdaFunctions.update(funcName, fileArr, options); 167 | }); 168 | 169 | program 170 | .command('roles') 171 | .addHelpText('before', intro('alanajs\n') 172 | + 'This will allow you to inteact with AWS IAM roles\n' 173 | + 'Usage Examples:\n\n ' 174 | + code('alana roles -l ') + '- list all the roles in AWS IAM\n ' 175 | + code('alana roles foobar ') + '- DOES NOT WORK. An interaction method must be used\n ' 176 | + code('alana roles -r foobar -c ') + '- creates a new role foobar to interact with Lambda\n ' 177 | + code('alana roles --delete foobar ') + '- will only delete the role foobar if it is not the default role\n ' 178 | + '\nSee below for the command line usage\n') 179 | .description('interact with AWS Roles') 180 | .argument('[awsRole]', 'the name of the AWS role', defaultRole) 181 | .option('-r, --role ', 'the name of the AWS role') 182 | .option('-c, --create', 'Creates role if it does not exist') 183 | .option('-l, --list', 'List all the roles in AWS') 184 | .option('--delete', 'delete the specified role') 185 | .action(async (awsRole, options) => { 186 | await roles(awsRole, options); 187 | }); 188 | 189 | program 190 | .command('buckets') 191 | .addHelpText('before', intro('alanajs\n') 192 | + 'This will allow you to inteact with AWS S3 buckets. S3 buckets are globally namespaced, so there may\n' 193 | + 'be another user that already has the specific named bucket you want to create. Additionally S3 bucket names\n' 194 | + 'will need to be alpha numeric and lowercase only. It will not create if it does not follow this naming convention\n' 195 | + 'Usage Examples:\n\n ' 196 | + code('alana buckets -l ') + '- list all the buckets in the user AWS S3\n ' 197 | + code('alana buckets -b foobar -c ') + '- creates a new bucket foobar\n ' 198 | + code('alana buckets --delete foobar ') + '- will only delete the bucket foobar if it is not the default role\n ' 199 | + '\nSee below for the command line usage\n') 200 | .description('interact with AWS S3 buckets') 201 | .argument('[s3bucket]', 'the name of the AWS S3 bucket', defaultBucket) 202 | .option('-b, --bucket ', 'S3 bucket name') 203 | .option('-c, --create', 'Create the bucket if it does not exist') 204 | .option('-l, --list', 'List all the buckets in S3') 205 | .option('--delete', 'delete the specified bucket') 206 | .action(async (s3bucket, options) => { 207 | // console.log(await s3.getBucketList()); 208 | await buckets(s3bucket, options); 209 | }); 210 | 211 | program 212 | .command('run') 213 | .addHelpText('before', intro('alanajs\n') 214 | + 'This will invoke a function with a potential version specified\n' 215 | + 'Usage Examples:\n\n ' 216 | + code('alana invoke foo ') + '- This will invoke the Lambda function foo\n ' 217 | + code('alana invoke foo 2 ') + '- This will invoke the Lambda function foo version 2\n ' 218 | + '\nSee below for the command line usage\n') 219 | .description('invokes an AWS Lambda function') 220 | .argument('', 'the name of the AWS Lambda function') 221 | .argument('[params...]', 'the parameters being passed into the AWS Lambda function') 222 | .option('-v, --version ', 'the version of the AWS Lambda function being invoked. Must exist') 223 | .action(async (funcName, params, options) => { 224 | lambdaFunctions.invoke(funcName, params, options); 225 | }); 226 | 227 | program 228 | .command('createLayer') 229 | .addHelpText('before', intro('alanajs\n') 230 | + 'This allows for creation of Lambda Layer dependencies. By default, the Layers need to be inside the folder\n' 231 | + 'nodejs/node_modules. This will automatically insert the files inside the correct directory structure\n' 232 | + 'Usage Examples:\n\n ' 233 | + code('alana createLayer layer1 file1.js ') + '- this will create a Lambda layer named layer1 from file1.js\n ' 234 | + '\nSee below for the command line usage\n') 235 | .description('creates an AWS Lambda layer') 236 | .argument('', 'name of the created layer') 237 | .argument('', 'files to be converted into a Lambda layer') 238 | .action(async(layerName, fileArr) => { 239 | await layers.create(layerName, fileArr); 240 | }); 241 | 242 | program 243 | .command('addLayerToFunc') 244 | .addHelpText('before', intro('alanajs\n') 245 | + 'This attaches a specific Lambda Layer to a specific Lambda function\n' 246 | + 'Usage Examples:\n\n ' 247 | + code('alana addLayerToFunc testFunc -l layer1 ') + '- this will attach the Lambda Layer layer1 to the function testFunc\n ' 248 | + '\nSee below for the command line usage\n') 249 | .description('adds AWS Lambda Layer to existant function') 250 | .argument('', 'name of function to append') 251 | .option('-l, --layerName ') 252 | .option('-v, --layerVersion ') 253 | .action(async(funcName, options) => { 254 | await layers.addLayersToFunc(funcName, options); 255 | }); 256 | 257 | 258 | program 259 | .command('alias') 260 | .addHelpText('before', intro('alanajs\n') 261 | + 'This lets you interact with Lambda function aliases\n' 262 | + 'Usage Examples:\n\n ' 263 | + code('alana alias foo 3 -c test ') + '- this will create an alias named test for the Lambda function foo version 3\n ' 264 | + code('alana alias test --delete alias3 ') + '- this will delete the alias alias3 from the Lambda function test\n ' 265 | + '\nSee below for the command line usage\n') 266 | .description('Create alias function for each Lamda function') 267 | .argument('', 'name of function to append') 268 | .argument('[version]', 'version of function to point') 269 | .option('-c, --create ', 'Create the alias name if it does not exist') 270 | .option('-u, --update ', 'Update the alias name') 271 | .option('--delete ', 'Delete the alias name') 272 | .action(async(funcName,version, options) => { 273 | 274 | if (Object.keys(options).length > 1) { 275 | console.log(error('Error: Please select 1 option.',options)); 276 | return; 277 | } 278 | await aliases(funcName, version, options); 279 | }); 280 | 281 | program 282 | .command('api') 283 | .addHelpText('before', intro('alanajs\n') 284 | + 'This lets you interact with API HTTP Gateway\n' 285 | + 'Usage Examples:\n\n ' 286 | + code('alana api ') + '- this will list all the APIs in AWS\n ' 287 | + code('alana api -c testapi ') + '- this will create an API named testapi\n ' 288 | + code('alana api -u testapi -d "this is a test api" ') + '- this will update testapi with a description "this is a test api"\n ' 289 | + code('alana api -u testapi -v 5 ') + '- this will update testapi with to the version 5"\n ' 290 | + code('alana api --delete testapi ') + '- this will delete the API named testapi\n ' 291 | + '\nSee below for the command line usage\n') 292 | .description('interact with the APIs') 293 | .argument('[apiName]', 'name of the api to get information on. Blank for all') 294 | .option('-c, --create', 'create the API named if it doesn\'t exist') 295 | .option('-u, --update', 'updates the API') 296 | .option('-v, --version ', 'specify the version of the api') 297 | .option('-d, --description ', 'the description of the api') 298 | .option('--delete', 'delete the api') 299 | .action(async (apiName, method, route, funcName, options) => { 300 | await apis.api(apiName, method, route, funcName, options); 301 | }); 302 | 303 | program 304 | .command('routes') 305 | .addHelpText('before', intro('alanajs\n') 306 | + 'This lets you interact with API HTTP Gateway routes\n' 307 | + 'Usage Examples:\n\n ' 308 | + code('alana routes -c GET . testFunc ') + '- this will create a GET method integration to testFunc at the root endpoint\n ' 309 | + code('alana routes -c POST valid validateData -d "description" ') + '- this will create a POST method integration to validateDate at the endpoint valid with a description\n ' 310 | + code('alana routes -u PUT abc foo ') + '- this will update the PUT method integration at root with the function foo\n ' 311 | + code('alana routes --delete GET . ') + '- this will delete the GET method integration at root\n ' 312 | + '\nSee below for the command line usage\n') 313 | .description('interact with a route on the API of choice.') 314 | .argument('', 'name of the api') 315 | .argument('[method]', 'type of HTTP request used to invoke') 316 | .argument('[route]', 'route to establish (use "." for root') 317 | .argument('[funcName]', 'the Lambda function that is invoked on the route') 318 | .option('-c, --create', 'create the route specified') 319 | .option('-u, --update', 'update the route specified') 320 | .option('-d, --description ', 'the description of the api') 321 | .option('--delete', 'delete the specified route') 322 | .action(async (apiName, method, route, funcName, options) => { 323 | await apis.routes(apiName, method, route, funcName, options); 324 | }); 325 | 326 | program 327 | .command('deploy') 328 | .addHelpText('before', intro('alanajs\n') 329 | + 'This lets you deploy an API gateway. This defaults to autodeploy so any future updates will be reflected\n' 330 | + 'Usage Examples:\n\n ' 331 | + code('alana deploy testapi ') + '- this will deploy the API testapi to a randomly generated stage name\n ' 332 | + code('alana deploy testapi production -d "live api" ') + '- this will deploy the API testapi to the stage named production with a description\n ' 333 | + '\nSee below for the command line usage\n') 334 | .description('deploy the api to a staged name') 335 | .argument('', 'name of the api') 336 | .argument('[stageName]', 'the name of the stage being deployed') 337 | .option('-d, --description ', 'the description of the stage being deployed') 338 | .action(async (apiName, stageName, options) => { 339 | await apis.deploy(apiName, stageName, options); 340 | }); 341 | } 342 | 343 | program.parse(); -------------------------------------------------------------------------------- /methods/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/alanajs/914e70cd2ab2ef781a88cbc205a3e670309002bd/methods/.DS_Store -------------------------------------------------------------------------------- /methods/AWS/deleteGatewayv2.js: -------------------------------------------------------------------------------- 1 | import { ApiGatewayV2Client, DeleteApiCommand, DeleteIntegrationCommand, DeleteRouteCommand } from '@aws-sdk/client-apigatewayv2'; 2 | import { AwsParams } from '../util/aws.js'; 3 | 4 | const apiGateway = new ApiGatewayV2Client(AwsParams); 5 | 6 | import { starting, error, fail, finished } from '../util/chalkColors.js'; 7 | 8 | const api = {}; 9 | 10 | api.deleteApi = async (params) => { 11 | console.log(starting(`Deleting the API "${params.Name}"`)); 12 | const data = await apiGateway.send(new DeleteApiCommand(params)) 13 | .then(data => { 14 | // console.log(data); 15 | console.log(finished(' Finished deleting API\n')); 16 | return data; 17 | }) 18 | .catch(err => { 19 | console.log(error('Error in deleting API: ', err.message)); 20 | return; 21 | }); 22 | 23 | return data; 24 | }; 25 | 26 | api.deleteIntegration = async (params) => { 27 | const data = await apiGateway.send(new DeleteIntegrationCommand(params)) 28 | .then(data => { 29 | console.log(finished(' Finished deleting integration\n')); 30 | return data; 31 | }) 32 | .catch(err => { 33 | console.log(error('Error in deleting integration: ', err.message)); 34 | return; 35 | }); 36 | 37 | return data; 38 | }; 39 | 40 | api.deleteRoute = async (params) => { 41 | const data = await apiGateway.send(new DeleteRouteCommand(params)) 42 | .then(data => { 43 | console.log(finished(' Finished deleting route\n')); 44 | return data; 45 | }) 46 | .catch(err => { 47 | console.log(error('Error in deleting route: ', err.message)); 48 | return; 49 | }); 50 | 51 | return data; 52 | }; 53 | 54 | export default api; -------------------------------------------------------------------------------- /methods/AWS/gatewayv2.js: -------------------------------------------------------------------------------- 1 | import { ApiGatewayV2Client, CreateApiCommand, CreateRouteCommand, CreateIntegrationCommand, CreateStageCommand, CreateDeploymentCommand, UpdateApiCommand, DeleteApiCommand } from '@aws-sdk/client-apigatewayv2'; 2 | import { AwsAccount, AwsParams, AwsRegion } from '../util/aws.js'; 3 | 4 | 5 | const apiGateway = new ApiGatewayV2Client(AwsParams); 6 | 7 | import { starting, error, fail, finished } from '../util/chalkColors.js'; 8 | 9 | 10 | const api = {}; 11 | 12 | api.createApi = async (params) => { 13 | params.Description ? console.log(starting(`Creating an API named "${params.Name}" with the description "${params.Sescription}"`)) : console.log(starting(`Creating an API named "${params.Name}"`)); 14 | 15 | const awsParams = { 16 | Name: params.Name, 17 | ProtocolType: 'HTTP', 18 | }; 19 | if (params.Description) awsParams.Description = params.Description; 20 | if (params.Version) awsParams.Version = params.Verion; 21 | 22 | const data = await apiGateway.send(new CreateApiCommand(awsParams)) 23 | .then(data => { 24 | console.log(finished(' Finished creating API\n')); 25 | return data; 26 | }) 27 | .catch(err => { 28 | console.log(error(' Error in creating API gateway: ', err.message)); 29 | return; 30 | }); 31 | 32 | return data; 33 | }; 34 | 35 | api.createIntegration = async (params) => { 36 | console.log(starting(`Creating an Lambda integration of function "${params.funcName}" to the API "${params.Name}"`)); 37 | 38 | const awsParams = { 39 | ApiId: params.ApiId, 40 | IntegrationMethod: 'POST', 41 | IntegrationUri: 'arn:aws:lambda:' + AwsRegion + ':' + AwsAccount + ':function:' + params.funcName, 42 | ConnectionType: 'INTERNET', 43 | IntegrationType: 'AWS_PROXY', 44 | PayloadFormatVersion: '2.0' 45 | 46 | }; 47 | const data = await apiGateway.send(new CreateIntegrationCommand(awsParams)) 48 | .then(data => { 49 | console.log(finished(' Finished creating integration\n')); 50 | return data; 51 | }) 52 | .catch(err => { 53 | console.log(error('Error in creating integration: ', err.message)); 54 | return; 55 | }); 56 | 57 | return data; 58 | }; 59 | 60 | api.createRoute = async (method, route, params) => { 61 | route ? console.log(starting(`Creating a "${method}" request to the route "${route}" in API #${params.Name}"`)) : console.log(starting(`Creating a "${method}" request to the main route of API #${params.Name}"`)) ; 62 | 63 | const awsParams = { 64 | ApiId: params.ApiId, 65 | RouteKey: method.toUpperCase() + ' /' + route, 66 | Target: params.Target 67 | }; 68 | const data = await apiGateway.send(new CreateRouteCommand(awsParams)) 69 | .then(data => { 70 | console.log(finished(' Finished creating route\n')); 71 | return data; 72 | }) 73 | .catch(err => { 74 | console.log(error('Error in creating route: ', err.message)); 75 | return; 76 | }); 77 | 78 | return data; 79 | }; 80 | 81 | 82 | api.createDeployment = async (params) => { 83 | console.log(starting(`Creating a deployment of "${params.Name}"`)); 84 | const awsParams = { 85 | ApiId: params.ApiId, 86 | }; 87 | 88 | const data = await apiGateway.send(new CreateDeploymentCommand(awsParams)) 89 | .then(data => { 90 | console.log(finished(' Finished creating deployment\n')); 91 | return data; 92 | }) 93 | .catch(err => { 94 | console.log(error('Error in creating deployment: ', err.message)); 95 | return; 96 | }); 97 | 98 | return data; 99 | }; 100 | 101 | api.createStage = async (params) => { 102 | 103 | console.log(starting(`Creating a staged deployment of "${params.Name}"`)); 104 | const awsParams = { 105 | ApiId: params.ApiId, 106 | DeploymentId: params.DeploymentId, 107 | AutoDeploy: true 108 | }; 109 | 110 | // it will be the user defined stage name or a random 6 character hex string 111 | params.StageName ? awsParams.StageName = params.StageName : awsParams.StageName = Math.random().toString(16).slice(2, 6); 112 | 113 | const data = await apiGateway.send(new CreateStageCommand(awsParams)) 114 | .then(data => { 115 | console.log(finished(' Finished creating staged deployment\n')); 116 | return data; 117 | }) 118 | .catch(err => { 119 | console.log(error('Error in creating staged deployment: ', err.message)); 120 | return; 121 | }); 122 | 123 | return data; 124 | }; 125 | 126 | api.updateApi = async (params) => { 127 | 128 | console.log(starting(`Updating the API "${params.Name}"`)); 129 | const awsParams = { 130 | ApiId: params.ApiId, 131 | Name: params.Name, 132 | Description: params.Description, 133 | Version: params.Version 134 | }; 135 | const data = await apiGateway.send(new UpdateApiCommand(params)) 136 | .then(data => { 137 | console.log(finished(' Finished updating API\n')); 138 | return data; 139 | }) 140 | .catch(err => { 141 | console.log(error('Error in updating API: ', err.message)); 142 | return; 143 | }); 144 | 145 | return data; 146 | }; 147 | 148 | export default api; -------------------------------------------------------------------------------- /methods/AWS/getGatewayv2.js: -------------------------------------------------------------------------------- 1 | import { ApiGatewayV2Client, GetApiCommand, GetApisCommand, GetRouteCommand, GetRoutesCommand, GetIntegrationCommand, GetIntegrationsCommand } from '@aws-sdk/client-apigatewayv2'; 2 | import { AwsParams } from '../util/aws.js'; 3 | 4 | const apiGateway = new ApiGatewayV2Client(AwsParams); 5 | 6 | import { starting, error, fail, finished } from '../util/chalkColors.js'; 7 | 8 | const getHTTPApi = {}; 9 | 10 | getHTTPApi.getApi = async (params) => { 11 | const data = await apiGateway.send(new GetApiCommand(params)) 12 | .then(data => { 13 | // console.log(data); 14 | return data; 15 | }) 16 | .catch(err => { 17 | console.log(error('Error in getting API information: ', err.message)); 18 | return; 19 | }); 20 | 21 | return data; 22 | }; 23 | 24 | getHTTPApi.getApis = async () => { 25 | console.log(starting('Getting a list of all APIs')); 26 | const data = await apiGateway.send(new GetApisCommand({})) 27 | .then(data => { 28 | // console.log(data); 29 | console.log(finished(' Finished getting APIs\n')); 30 | return data; 31 | }) 32 | .catch(err => { 33 | console.log(error('Error in getting APIs: ', err.message)); 34 | return; 35 | }); 36 | 37 | return data; 38 | }; 39 | 40 | getHTTPApi.getRoute = async (params) => { 41 | const awsParams = { 42 | ApiId: params.ApiId, 43 | RouteId: params.RouteId 44 | }; 45 | const data = await apiGateway.send(new GetRouteCommand(awsParams)) 46 | .then(data => { 47 | // console.log(data); 48 | return data; 49 | }) 50 | .catch(err => { 51 | console.log(error('Error in getting route information: ', err.message)); 52 | return; 53 | }); 54 | 55 | return data; 56 | }; 57 | 58 | getHTTPApi.getRoutes = async (params) => { 59 | console.log(starting('Getting a list of all routes')); 60 | const awsParams = { 61 | ApiId: params.ApiId, 62 | }; 63 | const data = await apiGateway.send(new GetRoutesCommand(awsParams)) 64 | .then(data => { 65 | // console.log(data); 66 | console.log(finished(' Finished getting routes\n')); 67 | return data; 68 | }) 69 | .catch(err => { 70 | console.log(error('Error in getting routes: ', err.message)); 71 | return; 72 | }); 73 | 74 | return data; 75 | }; 76 | 77 | getHTTPApi.getIntegration = async (params) => { 78 | const awsParams = { 79 | ApiId: params.ApiId, 80 | IntegrationId: params.IntegrationId, 81 | }; 82 | 83 | const data = await apiGateway.send(new GetIntegrationCommand(awsParams)) 84 | .then(data => { 85 | // console.log(data); 86 | return data; 87 | }) 88 | .catch(err => { 89 | console.log(error('Error in getting integration: ', err.message)); 90 | return; 91 | }); 92 | 93 | return data; 94 | }; 95 | 96 | getHTTPApi.getIntegrations = async (params) => { 97 | console.log(starting('Getting a list of all integrations')); 98 | const awsParams = { 99 | ApiId: params.ApiId, 100 | IntegrationId: params.IntegrationId, 101 | IntegrationResponseId: params.IntegrationResponseId 102 | }; 103 | const data = await apiGateway.send(new GetIntegrationsCommand(awsParams)) 104 | .then(data => { 105 | // console.log(data); 106 | console.log(finished(' Finished getting integrations\n')); 107 | return data; 108 | }) 109 | .catch(err => { 110 | console.log(error('Error in getting integrations: ', err.message)); 111 | return; 112 | }); 113 | 114 | return data; 115 | }; 116 | 117 | export default getHTTPApi; -------------------------------------------------------------------------------- /methods/AWS/iam.js: -------------------------------------------------------------------------------- 1 | import { IAMClient, CreateRoleCommand, AttachRolePolicyCommand, ListRolesCommand, DeleteRoleCommand } from '@aws-sdk/client-iam'; 2 | import { AwsParams, AwsRole, BasicPolicy, LambdaBasicARN } from '../util/aws.js'; 3 | 4 | import { starting, code, error, finished } from '../util/chalkColors.js'; 5 | 6 | // create the lambda client 7 | const iamClient = new IAMClient(AwsParams); 8 | 9 | const iam = {}; 10 | 11 | iam.getRoleList = async () => { 12 | console.log(starting('Getting a list of the AWS roles')); 13 | 14 | const data = await iamClient.send(new ListRolesCommand({})) 15 | .catch(err => { 16 | console.log(error(`Error while getting AWS roles: ${err.message}`)); 17 | return; 18 | }); 19 | 20 | return data.Roles; 21 | }; 22 | 23 | /** 24 | * @FuncName: verifyRole 25 | * @Description: ASYNC. This will check to see if a role in aws exists 26 | * @input:roleName - a string containing the role name 27 | * @output: boolean -if the role exists 28 | */ 29 | iam.verifyRole = async (roleName = AwsRole) => { 30 | console.log(starting(`Verifying the AWS Role named ${roleName}`)); 31 | 32 | const data = await iamClient.send(new ListRolesCommand({})) 33 | .catch(err => { 34 | console.log(error(`Error while verifying AWS role: ${err.message}`)); 35 | return; 36 | }); 37 | // iterate through array and check Name against bucket 38 | for (const el of data.Roles) { 39 | if (el.RoleName === roleName) return true; 40 | } 41 | return false; 42 | }; 43 | 44 | /** 45 | * @FuncName: createRole 46 | * @Description: ASYNC. This will create a role in aws for the user to invoke lambda functions 47 | * @input: roleName - a string containing the role name 48 | */ 49 | iam.createRole = async (roleName = AwsRole) => { 50 | console.log(starting(`Creating a new AWS Role named ${roleName}`)); 51 | 52 | // creating the params for an aws role with no policies attached 53 | const roleParams = { 54 | AssumeRolePolicyDocument: JSON.stringify(BasicPolicy), 55 | RoleName: roleName 56 | }; 57 | 58 | await iamClient.send(new CreateRoleCommand(roleParams)) 59 | .then(data => { 60 | // console.log(data); 61 | console.log(' Created a new role.'); 62 | }) 63 | .catch(err => { 64 | console.log(error(`Error while creating a new AWS role: ${err.message}`)); 65 | return; 66 | }); 67 | 68 | 69 | // create the params to add the lambda basic execution role for global functions 70 | const arnParams = { 71 | RoleName: roleName, 72 | // default policy for lambda functions 73 | PolicyArn: LambdaBasicARN 74 | }; 75 | await iamClient.send(new AttachRolePolicyCommand(arnParams)) 76 | .then(data => { 77 | // console.log(data) 78 | console.log(' Applied the basic Lambda policy to new role'); 79 | console.log(finished(' Finished creating new AWS role with attached basic Lambda functionality policy')); 80 | return; 81 | }) 82 | .catch(err => { 83 | console.log(error(`Error while assigning policy to the new AWS role: ${err.message}`)); 84 | return; 85 | }); 86 | 87 | }; 88 | 89 | /** 90 | * @FuncName: deleteRole 91 | * @Description: ASYNC. This will delete a role in AWS 92 | * @input: roleName - a string containing the role name 93 | */ 94 | 95 | iam.deleteRole = async (role) => { 96 | const params = { 97 | RoleName: role 98 | }; 99 | 100 | const data = await iamClient.send(new DeleteRoleCommand(params)) 101 | .catch(err => { 102 | console.log(error(`Error while deleting AWS role : ${err.message}`)); 103 | return; 104 | }); 105 | return data; 106 | }; 107 | 108 | export default iam; -------------------------------------------------------------------------------- /methods/AWS/lambda.js: -------------------------------------------------------------------------------- 1 | import { LambdaClient, 2 | ListFunctionsCommand, 3 | CreateFunctionCommand, 4 | InvokeCommand, 5 | UpdateFunctionCodeCommand, 6 | DeleteFunctionCommand, 7 | ListVersionsByFunctionCommand, 8 | PublishLayerVersionCommand, 9 | CreateAliasCommand, 10 | UpdateFunctionConfigurationCommand, 11 | UpdateAliasCommand, 12 | DeleteAliasCommand, 13 | GetFunctionConfigurationCommand, 14 | AddPermissionCommand, 15 | RemovePermissionCommand, 16 | GetPolicyCommand } from '@aws-sdk/client-lambda'; 17 | 18 | import path from 'path'; 19 | 20 | import { AwsRole, AwsParams, AwsBucket, AwsAccount, AwsRegion } from '../util/aws.js'; 21 | import { intro, starting, error, fail, finished, code } from '../util/chalkColors.js'; 22 | 23 | // create the lambda client 24 | const lambdaClient = new LambdaClient(AwsParams); 25 | 26 | const lambda = {}; 27 | 28 | /** 29 | * @FuncName: getFuncList 30 | * @Description: this will send a command to get all the function names 31 | * @output: functionList - an array of function names as strings 32 | * 33 | */ 34 | lambda.getFuncList = async () => { 35 | console.log(starting('Getting a list of Lambda functions')); 36 | //parameters for lambda command 37 | const params = { FunctionVersion: 'ALL' }; 38 | //sends a command via lambdaClient to list all functions 39 | const data = await lambdaClient.send(new ListFunctionsCommand(params)) 40 | .catch(err => { 41 | console.log(error('Error in getting the Lambda Function list: ', err)); 42 | }); 43 | 44 | if (!data) return; 45 | //parses out the function names from the functionList into a console.table object 46 | const functionList = {}; 47 | 48 | // creates a class called lambdaFunc 49 | function lambdaFunc(description, version, lastModified) { 50 | this.Description = description; 51 | this.Version = version; 52 | this.LastModified = lastModified; 53 | } 54 | 55 | data.Functions.map((el) => { 56 | functionList[el.FunctionName] = new lambdaFunc(el.Description, el.Version, el.LastModified.toLocaleString()); 57 | }); 58 | // res.locals.functionList = functionList; 59 | return functionList; 60 | }; 61 | 62 | lambda.getFuncVersionList = async (funcName) => { 63 | console.log(starting(`Getting a list of versions of Lambda function "${funcName}"`)); 64 | const params = {FunctionName: funcName}; 65 | const data = await lambdaClient.send(new ListVersionsByFunctionCommand(params)) 66 | .catch(err => { 67 | console.log(error('Error in getting the Lambda Function versions: ', err.message)); 68 | }); 69 | if (!data) return; 70 | console.log('this is funcversionlist data',data); 71 | // NOT FORMATTED TO A TABLE YET 72 | return data; 73 | }; 74 | 75 | /** 76 | * @FuncName: invoke 77 | * @Description: this will invoke the function specified in the parameters 78 | * @input:FuncName - the name of the function 79 | * @params - the parameters for the function 80 | * @output: the invocation response 81 | */ 82 | lambda.invoke = (funcName, params, options = {}) => { 83 | // destructure and set defaults to options if not included; 84 | const {bucket = AwsBucket, description = undefined, publish = false} = options; 85 | options.version ? console.log(starting(`Invoking the function "${funcName}" with the Qualifier "${options.version}"`)) : console.log(starting(`Invoking the function "${funcName}"`)); 86 | //input parameters for running the aws lambda function 87 | const lambdaParams = { 88 | //needed function name 89 | FunctionName: funcName, 90 | 91 | // pass in arguments for the lambda function (input payload) 92 | Payload: JSON.stringify(params), 93 | 94 | //default options that we may not need to change 95 | InvocationType: 'RequestResponse', 96 | LogType: 'Tail', 97 | }; 98 | if (options.version) lambdaParams.Qualifier = options.version; 99 | 100 | // invokecommand is a class that lets lambdaclient know that we want to run the function that is specified in the params 101 | lambdaClient.send(new InvokeCommand(lambdaParams)) 102 | .then(data => { 103 | // console.log(data); 104 | 105 | //This will output the invocation data log into a readable string 106 | // console.log(Buffer.from(data.LogResult,'base64').toString('ascii')); 107 | 108 | // lambda client returns data.payload which is utf8 and needs to be decoded and parsed 109 | const response = JSON.parse(new TextDecoder('utf-8').decode(data.Payload)); 110 | console.log(response); 111 | return response; 112 | }) 113 | .catch(err => { 114 | console.log(error('Error in invoke: ', err.message)); 115 | return err; 116 | }); 117 | }; 118 | 119 | /** 120 | * @FuncName: createFunction 121 | * @Description: this will create the function based on the file given in the S3 bucket 122 | * @input:funcName - the name of the function, user input, :outputZip - the file name of the zip file 123 | * 124 | */ 125 | lambda.createFunction = async(outputZip, funcName, options = {}) => { 126 | 127 | // destructure and set defaults to options if not included; 128 | const {role = AwsRole, bucket = AwsBucket, description = undefined, layerArr = null, publish = false} = options; 129 | 130 | console.log(starting(`Creating the function "${funcName}" from the output file "${outputZip}" found in the S3 Bucket "${bucket}"`)); 131 | 132 | // parameters for lambda command 133 | const params = { 134 | Code: {S3Bucket: bucket, S3Key: outputZip }, 135 | FunctionName: funcName, 136 | Runtime: 'nodejs14.x', 137 | Handler: 'index.handler', 138 | Role: `arn:aws:iam::${AwsAccount}:role/${role}`, 139 | Description: description, 140 | Publish: publish, 141 | Layers: layerArr 142 | }; 143 | 144 | const layerConfig = []; 145 | if(layerArr){ 146 | 147 | for (let i = 0; i < layerArr.length; i++){ 148 | const layerName = layerArr[i].layerName; 149 | const layerVersion = layerArr[i].layerVersion; 150 | layerConfig.push(`arn:aws:lambda:${AwsRegion}:${AwsAccount}:layer:${layerName}:${layerVersion}`); 151 | } 152 | if(layerConfig.length > 0) params.Layers = layerConfig; 153 | } 154 | 155 | //sends a command via lambdaClient to create a function 156 | 157 | return await lambdaClient.send(new CreateFunctionCommand(params)) 158 | .then(data => { 159 | console.log(finished(' Finished creating the function in Lambda.\n')); 160 | return data; 161 | }) 162 | .catch(err => { 163 | console.log(error('\n Error in lambda CreateFunctionCommand: ', err.message)); 164 | return err; 165 | }); 166 | }; 167 | 168 | /** 169 | * @FuncName: updateFunction 170 | * @Description: this will update the function FunctionName based on the file given in the S3 bucket 171 | * @input:funcName - the name of the function, user input :outputZip - the file name of the zip file 172 | */ 173 | 174 | lambda.updateFunction = async (outputZip, funcName, options = {}) => { 175 | // destructure options 176 | const {bucket = AwsBucket, publish = true} = options; 177 | 178 | console.log(' using lambdaController.updateFunction'); 179 | 180 | // params for lambda command 181 | const params = { 182 | FunctionName: funcName, 183 | Publish: publish, 184 | S3Bucket: bucket, 185 | S3Key: path.basename(outputZip), 186 | }; 187 | 188 | // send the update function command 189 | const data = await lambdaClient.send(new UpdateFunctionCodeCommand(params)) 190 | .then(data => { 191 | // console.log(data); 192 | return data; 193 | }) 194 | .catch(err => { 195 | console.log(error('Error in lambda updateFunctionCode:', err.message)); 196 | return err; 197 | }); 198 | return data; 199 | }; 200 | 201 | /** 202 | * @FuncName: deleteFunction 203 | * @Description: this will delete the function with the specified name 204 | * @input: funcName - the name of the function, user input 205 | */ 206 | lambda.deleteFunction = async (funcName, qualifier) => { 207 | qualifier ? console.log(starting(`Deleting the function "${funcName}" with the Qualifier "${qualifier}"`)) : console.log(starting(`Deleting the function "${funcName}"`)); 208 | 209 | // parameters for lambda command 210 | const params = { 211 | FunctionName: funcName, 212 | }; 213 | 214 | //qualifier: optional version to delete 215 | if(qualifier) params.Qualifier = qualifier; 216 | 217 | return await lambdaClient.send(new DeleteFunctionCommand(params)) 218 | .then(data => { 219 | // console.log(data); 220 | return data; 221 | }) 222 | .catch(err => { 223 | console.log(error('Error in lambda DeleteFunctionCommand: ', err.message)); 224 | return err; 225 | }); 226 | }; 227 | 228 | 229 | 230 | /** 231 | * @FuncName: createLambdaLayer 232 | * @Description: this will create a lambda layer with the specified name and code 233 | * @Input: layername - string that contains layername, :outputZip - file name of the zip file 234 | */ 235 | lambda.createLambdaLayer = async (layerName, outputZip) => { 236 | console.log(' using lambda.addLambdaLayers'); 237 | 238 | const params = { 239 | Content: {S3Bucket: AwsBucket, S3Key: outputZip}, 240 | LayerName: layerName 241 | }; 242 | 243 | return await lambdaClient.send(new PublishLayerVersionCommand(params)) 244 | .then(data => { 245 | return data; 246 | }) 247 | .catch(err => { 248 | console.log('Error in lambda PublishLayerVersionCommand: ', err); 249 | }); 250 | }; 251 | 252 | /** 253 | * @FuncName: addLayerToFunc 254 | * @Description: this will add AWS lambda layers to specified function 255 | * @input: funcName - string that contains name of function, 256 | */ 257 | lambda.addLayerToFunc = async (funcName, layerArr) => { 258 | console.log('using lambda.addLayerToFunc'); 259 | 260 | console.log('the layerArr is ', layerArr); 261 | 262 | const params = { 263 | FunctionName : funcName 264 | }; 265 | 266 | const layerConfig = []; 267 | if(layerArr){ 268 | 269 | for (let i = 0; i < layerArr.length; i++){ 270 | const layerName = layerArr[i].layerName; 271 | const layerVersion = layerArr[i].layerVersion; 272 | layerConfig.push(`arn:aws:lambda:${AwsRegion}:${AwsAccount}:layer:${layerName}:${layerVersion}`); 273 | } 274 | if(layerConfig.length > 0) params.Layers = layerConfig; 275 | console.log(params.Layers); 276 | } 277 | 278 | return await lambdaClient.send(new UpdateFunctionConfigurationCommand(params)) 279 | .then(data => { 280 | return data; 281 | }) 282 | .catch(err => { 283 | console.log('Error in lambda updateFunctionConfigurationCommand: ', err); 284 | }); 285 | }; 286 | 287 | //FunctionVersion: Func Version that alias invoked 288 | //name: Name of the Alias 289 | lambda.createAlias = async(funcName, version, aliasName = 'aliasName') => { 290 | 291 | // params for lambda command 292 | const params = { 293 | FunctionName: funcName, 294 | FunctionVersion : version, 295 | Name: aliasName 296 | }; 297 | 298 | // send the new alias 299 | return await lambdaClient.send(new CreateAliasCommand(params)) 300 | .then(data => { 301 | // console.log(data); 302 | return data; 303 | }) 304 | .catch(err => { 305 | console.log(error('Error in lambda createAliasCommand:', err.message)); 306 | return err; 307 | }); 308 | }; 309 | 310 | lambda.updateAlias = async(funcName, version, aliasName) => { 311 | 312 | // params for lambda command 313 | const params = { 314 | FunctionName: funcName, 315 | FunctionVersion : version, 316 | Name: aliasName 317 | }; 318 | 319 | // send the new alias 320 | return await lambdaClient.send(new UpdateAliasCommand(params)) 321 | .then(data => { 322 | // console.log(data); 323 | return data; 324 | }) 325 | .catch(err => { 326 | console.log(error('Error in lambda updateAliasCommand:', err.message)); 327 | return err; 328 | }); 329 | }; 330 | 331 | lambda.deleteAlias = async(funcName, aliasName = 'aliasName') => { 332 | 333 | // params for lambda command 334 | const params = { 335 | FunctionName: funcName, 336 | Name: aliasName 337 | }; 338 | 339 | // send the new alias 340 | return await lambdaClient.send(new DeleteAliasCommand(params)) 341 | .then(data => { 342 | // console.log(data); 343 | return data; 344 | }) 345 | .catch(err => { 346 | console.log(error('Error in lambda updateAliasCommand:', err.message)); 347 | return err; 348 | }); 349 | }; 350 | 351 | lambda.getFuncConfig = async (funcName) => { 352 | const params = { 353 | FunctionName: funcName 354 | }; 355 | 356 | await lambdaClient.send(new GetFunctionConfigurationCommand(params)) 357 | .then(data => { 358 | console.log(data); 359 | return data; 360 | }) 361 | .catch(err => { 362 | console.log('Error in lambda getFunctionConfigurationCommand: ', err.message); 363 | }); 364 | }; 365 | 366 | lambda.addPermission = async (funcName, apiId, route) => { 367 | console.log(starting(`Adding API permissions to "${funcName}"`)); 368 | const params = { 369 | StatementId: funcName + Date.now().toString(), 370 | Action: 'lambda:InvokeFunction', 371 | FunctionName: `arn:aws:lambda:${AwsRegion}:${AwsAccount}:function:${funcName}`, 372 | Principal: 'apigateway.amazonaws.com', 373 | SourceArn: `arn:aws:execute-api:${AwsRegion}:${AwsAccount}:${apiId}/*/*/` 374 | }; 375 | 376 | if (route) params.SourceArn = params.SourceArn + `${route}`; 377 | 378 | const data = await lambdaClient.send(new AddPermissionCommand(params)) 379 | .then(data => { 380 | // console.log(data); 381 | console.log(finished(' Finished adding permissions\n')); 382 | return data; 383 | }) 384 | .catch(err => { 385 | console.log(error('Error in adding permissions: ', err.message)); 386 | return; 387 | }); 388 | 389 | return data; 390 | }; 391 | 392 | lambda.removePermission = async (funcName, statementId) => { 393 | console.log(starting(`Removing API permissions to "${funcName}"`)); 394 | const params = { 395 | StatementId: statementId, 396 | FunctionName: `arn:aws:lambda:${AwsRegion}:${AwsAccount}:function:${funcName}`, 397 | }; 398 | 399 | const data = await lambdaClient.send(new RemovePermissionCommand(params)) 400 | .then(data => { 401 | // console.log(data); 402 | console.log(finished(' Finished removing permissions\n')); 403 | return data; 404 | }) 405 | .catch(err => { 406 | console.log(error('Error in removing permissions: ', err.message)); 407 | return; 408 | }); 409 | 410 | return data; 411 | }; 412 | 413 | lambda.getPolicy = async (funcName, qualifier = undefined) => { 414 | console.log(starting(`Getting permission policy of "${funcName}"`)); 415 | const params = { 416 | FunctionName: funcName, 417 | Qualifier: qualifier 418 | }; 419 | 420 | const data = await lambdaClient.send(new GetPolicyCommand(params)) 421 | .then(data => { 422 | // console.log(data); 423 | console.log(finished(' Finished getting permission policy\n')); 424 | return data; 425 | }) 426 | .catch(err => { 427 | console.log(error('Error in getting permission policy: ', err.message)); 428 | return; 429 | }); 430 | 431 | return data; 432 | }; 433 | 434 | export default lambda; -------------------------------------------------------------------------------- /methods/AWS/s3.js: -------------------------------------------------------------------------------- 1 | import { S3Client, PutObjectCommand, CreateBucketCommand, GetBucketAclCommand, ListBucketsCommand, DeleteBucketCommand } from '@aws-sdk/client-s3'; 2 | import path from 'path'; 3 | import fs from 'fs'; 4 | 5 | import {AwsParams, AwsBucket} from '../util/aws.js'; 6 | import { starting, code, error, finished } from '../util/chalkColors.js'; 7 | 8 | // create the s3 client 9 | const s3Client = new S3Client(AwsParams); 10 | 11 | const s3 = {}; 12 | 13 | /** 14 | * ASYNC Get a list of all the S3 buckets under the user's account 15 | * @returns (array) A list of all S3 buckets 16 | */ 17 | s3.getBucketList = async () => { 18 | console.log(starting('Getting the list of S3 buckets')); 19 | 20 | const data = await s3Client.send(new ListBucketsCommand({})) 21 | .catch(err => { 22 | console.log(error(`Error while getting S3 bucket list: ${err.message}`)); 23 | return; 24 | }); 25 | return data.Buckets; 26 | }; 27 | 28 | /** 29 | * ASYNC. Verifies whether or not a bucket named bucketName exists in the user's S3 namespace 30 | * @param {*} bucketName (string) a string containing the bucket name, or the default bucket name 31 | * @returns (boolean) whether or not the bucket specified in input exists in S3 32 | */ 33 | s3.verifyBucket = async (bucketName = AwsBucket) => { 34 | console.log(starting(`Verifying the AWS S3 bucket named "${bucketName}"`)); 35 | 36 | // get a list of buckets 37 | const data = await s3Client.send(new ListBucketsCommand({})) 38 | .catch(err => { 39 | console.log(error(`Error while getting S3 bucket list: ${err.message}`)); 40 | return; 41 | }); 42 | 43 | // iterate through array and check Name against bucket 44 | for (const el of data.Buckets) { 45 | if (el.Name === bucketName) return true; 46 | } 47 | return false; 48 | }; 49 | 50 | /** 51 | * ASYNC. This creates an S3 bucket named bucketName 52 | * @param {*} bucketName - (string) The name of the bucket to be created 53 | * @returns (various) undefined if there's an error. The AWS response object if command is sent properly. 54 | 55 | */ 56 | s3.createBucket = async (bucketName = AwsBucket) => { 57 | console.log(starting(`Creating an AWS S3 bucket named "${bucketName}"`)); 58 | 59 | // params needed to create a s3 bucket 60 | const params = { 61 | // bucket name 62 | Bucket: bucketName, 63 | }; 64 | 65 | // create the bucket 66 | // Amazon S3 bucket names must be unique globally. If you get the "Bucket name already exists" or "BucketAlreadyExists" error, 67 | // then you must use a different bucket name to create the bucket. These error messages indicate that another AWS account owns a bucket with the same name. 68 | const response = await s3Client.send(new CreateBucketCommand(params)) 69 | .then(data => { 70 | // do something with data 71 | console.log(finished(' Finished creating a new S3 bucket.\n')); 72 | // console.log(data); 73 | return data; 74 | }) 75 | .catch(err => { 76 | console.log(error(`There's an error with creating an S3 bucket: ${err.message}`)); 77 | if (err.message === 'BucketAlreadyExists') { 78 | console.log(error('Amazon S3 bucket names must be unique globally. If you get the "Bucket name already exists" or "BucketAlreadyExists" error, then you must use a different bucket name to create the bucket. These error messages indicate that another AWS account owns a bucket with the same name.')); 79 | } 80 | return; 81 | }); 82 | return response; 83 | }; 84 | 85 | 86 | /** 87 | * ASYNC. This sends a file outputZip to the S3 bucket bucketName 88 | * @param {*} outputZip - (string) zip file name that will be sent to S3 89 | * @param {*} bucketName - (string) bucket where the zip file is being sent to 90 | * @returns (various) undefined if there's an error. OutputZip if command is sent properly. 91 | */ 92 | s3.sendFile = async (outputZip, bucketName = AwsBucket) => { 93 | console.log(starting(`Sending the file "${outputZip}" to the AWS S3 Bucket "${bucketName}"`)); 94 | // creates a file stream of the zip file 95 | const fileStream = fs.createReadStream(outputZip); 96 | 97 | const params = { 98 | // s3 bucket 99 | Bucket: bucketName, 100 | // Add the required 'Key' parameter using the 'path' module. 101 | Key: path.basename(outputZip), 102 | // Add the required 'Body' parameter 103 | Body: fileStream, 104 | }; 105 | const data = await s3Client.send(new PutObjectCommand(params)) 106 | .then(data => { 107 | // console.log(data); 108 | console.log(finished(' Finished sending file.\n')); 109 | return outputZip; 110 | }) 111 | .catch(err => { 112 | // console.log(err); 113 | console.log(error(`Error sending file to the S3 bucket : ${err.message}`)); 114 | return; 115 | }); 116 | return data; 117 | }; 118 | 119 | 120 | /** 121 | * ASYNC. This will send a command to AWS S3 to delete a bucket 122 | * @param {*} bucketName - (string) The name of the bucket to be deleted 123 | * @returns (object) the AWS metadata from the command 124 | */ 125 | s3.deleteBucket = async (bucketName) => { 126 | console.log(starting(`Deleting the AWS S3 bucket named "${bucketName}"`)); 127 | const params = { 128 | Bucket: bucketName 129 | }; 130 | 131 | const data = await s3Client.send(new DeleteBucketCommand(params)) 132 | .then(data => { 133 | // console.log(data); 134 | console.log(finished(' Finished deleting the bucket.\n')); 135 | return data; 136 | }) 137 | .catch(err => { 138 | console.log(error(`Problem with deleting S3 bucket : ${err.message}`)); 139 | return; 140 | }); 141 | return data; 142 | }; 143 | 144 | export default s3; -------------------------------------------------------------------------------- /methods/commands/aliases.js: -------------------------------------------------------------------------------- 1 | import lambda from '../AWS/lambda.js'; 2 | 3 | import { intro, starting, error, fail, finished, code } from '../util/chalkColors.js'; 4 | 5 | const aliases = async (funcName, version, options) => { 6 | if (options.create){ 7 | const aliasName = options.create; 8 | console.log(starting('Sending request to AWS Lambda...')); 9 | const response = await lambda.createAlias(funcName, version, aliasName); 10 | if (response.$metadata.httpStatusCode < 300) console.log(finished('Request complete: Alias created')); 11 | } 12 | 13 | else if (options.update){ 14 | const aliasName = options.update; 15 | console.log(starting('Sending request to AWS Lambda...')); 16 | const response = await lambda.updateAlias(funcName, version, aliasName); 17 | if (response.$metadata.httpStatusCode < 300) console.log(finished('Request complete: Alias updated')); 18 | } 19 | 20 | else if (options.delete){ 21 | const aliasName = options.delete; 22 | console.log(starting('Sending request to AWS Lambda...')); 23 | const response = await lambda.deleteAlias(funcName, aliasName); 24 | if (response.$metadata.httpStatusCode < 300) console.log(finished('Request complete: Alias deleted')); 25 | } 26 | }; 27 | 28 | 29 | export default aliases; -------------------------------------------------------------------------------- /methods/commands/apis.js: -------------------------------------------------------------------------------- 1 | import api, {default as awsApi } from '../AWS/gatewayv2.js'; 2 | import {default as getApi } from '../AWS/getGatewayv2.js'; 3 | import {default as deleteApi } from '../AWS/deleteGatewayv2.js'; 4 | 5 | import lambda from '../AWS/lambda.js'; 6 | import { AwsRegion, AwsAccount } from '../util/aws.js'; 7 | 8 | //NEED TO ADD THIS TO THE REST OF THE PRODUCT 9 | 10 | import { starting, code, error, finished } from '../util/chalkColors.js'; 11 | import { verifyFunction } from '../util/verifyAWS.js'; 12 | 13 | const methods = ['ANY', 'GET', 'POST', 'PUT', 'PATCH', 'HEAD', 'DELETE', 'OPTIONS']; 14 | 15 | const verifyMethod = (method) => { 16 | method = method.toUpperCase(); 17 | if (!methods.includes(method)) { 18 | console.log(error('The request method specified is an invalid HTTP request. The only valid requests are: ')); 19 | console.log(' ', code(methods)); 20 | return false; 21 | } 22 | return method; 23 | }; 24 | 25 | const getApiId = async (apiName) => { 26 | const getApisResponse = await getApi.getApis(); 27 | 28 | // get the API ID from the name 29 | for (const item of getApisResponse.Items) { 30 | if (item.Name === apiName) { 31 | return item.ApiId; 32 | } 33 | } 34 | console.log(error('No matching API name.')); 35 | return false; 36 | }; 37 | const apis = {}; 38 | 39 | apis.routes = async(apiName, method, route, funcName, options) => { 40 | if (!options) await apis.getRoutes(apiName); 41 | 42 | if (funcName) { 43 | const validFunction = await verifyFunction(funcName); 44 | if (!validFunction) return; 45 | } 46 | if (options.create) { 47 | await apis.createRoute(apiName, method, route, funcName, options); 48 | return; 49 | } 50 | else if (options.update) { 51 | await apis.deleteRoute(apiName, method, route); 52 | await apis.createRoute(apiName, method, route, funcName, options); 53 | return; 54 | } 55 | else if (options.delete) { 56 | await apis.deleteRoute(apiName, method, route); 57 | return; 58 | } 59 | }; 60 | 61 | apis.createApi = async (apiName, options) => { 62 | const params = { 63 | Name: apiName 64 | }; 65 | 66 | // add on optional params 67 | if (options.description) params.description = options.description; 68 | 69 | return await awsApi.createApi(params); 70 | }; 71 | 72 | apis.api = async (apiName, options) => { 73 | if (!apiName) { 74 | const apis = await getApi.getApis(); 75 | for (const item of apis.Items) { 76 | console.log('Name:', item.Name); 77 | if (item.Version) console.log('Version:', item.Version); 78 | console.log('Created Date:', item.CreatedDate); 79 | console.log('Description:', item.Description); 80 | console.log('API Endpoint:', item.ApiEndpoint); 81 | console.log('\n'); 82 | } 83 | return; 84 | } 85 | 86 | const params = { 87 | Name: apiName, 88 | }; 89 | if (options.description) params.Description = options.description; 90 | if (options.version) params.Version = options.version; 91 | 92 | if (options.create) { 93 | await apis.createApi(apiName, params); 94 | return; 95 | } 96 | const ApiId = await getApiId(apiName); 97 | if (!ApiId) return; 98 | params.ApiId = ApiId; 99 | 100 | if (options.update) { 101 | await api.updateApi(params); 102 | return; 103 | } 104 | if (options.delete) { 105 | await deleteApi.deleteApi(params); 106 | return; 107 | } 108 | else { 109 | const apiInformation = await getApi.getApi(params); 110 | console.log(apiInformation); 111 | } 112 | }; 113 | apis.createRoute = async (apiName, method, route, funcName, options) => { 114 | // verify that method type is accurate 115 | method = verifyMethod(method); 116 | if (!method) return; 117 | 118 | // verify route. Set route to main route if it's '.' 119 | if (route === '*') { 120 | console.log(error('Invalid route')); 121 | return; 122 | } 123 | if (route === '.') route = ''; 124 | 125 | const outputParams = {}; 126 | 127 | const params = { 128 | Name: apiName, 129 | funcName: funcName 130 | }; 131 | 132 | params.ApiId = await getApiId(apiName); 133 | if (!params.ApiId) return; 134 | 135 | // create the integration 136 | const createIntegrationResponse = await awsApi.createIntegration(params); 137 | if (!createIntegrationResponse) return; 138 | else { 139 | // add integration ID to params 140 | params.IntegrationId = createIntegrationResponse.IntegrationId; 141 | params.Target = 'integrations/' + createIntegrationResponse.IntegrationId; 142 | } 143 | // create the method 144 | const createRouteResponse = await awsApi.createRoute(method, route, params); 145 | if (!createRouteResponse) return; 146 | 147 | // adds the permission 148 | const addPermissionResponse = await lambda.addPermission(funcName, params.ApiId, route); 149 | if (!addPermissionResponse) return; 150 | 151 | // 152 | route ? console.log(`A ${method} request to ${route} has been created.`) : console.log(`A ${method} request to root has been created.`); 153 | }; 154 | 155 | 156 | apis.getRoutes = async (apiName) => { 157 | const output = []; 158 | const params = {}; 159 | 160 | params.ApiId = await getApiId(apiName); 161 | if (!params.ApiId) return; 162 | 163 | // get all the routes 164 | const getRoutesResponse = await getApi.getRoutes(params); 165 | 166 | console.log(getRoutesResponse); 167 | // iterate over the routes 168 | for (const item of getRoutesResponse.Items) { 169 | // get the itegration IDs 170 | const integrationParams = { 171 | ApiId: params.ApiId, 172 | IntegrationId: item.Target.slice(13) 173 | }; 174 | 175 | // get the information from the integration 176 | const getIntegrationResponse = await getApi.getIntegration(integrationParams); 177 | 178 | // parse out the info to respond into an array for a console table 179 | const apiRoute = {}; 180 | const routeBreak = item.RouteKey.indexOf('/'); 181 | apiRoute.Method = item.RouteKey.slice(0, routeBreak - 1); 182 | apiRoute.Route = item.RouteKey.slice(routeBreak + 1); 183 | const integrationUri = getIntegrationResponse.IntegrationUri; 184 | const functinBreak = integrationUri.indexOf('function:') + 9; 185 | apiRoute.FunctionName = integrationUri.slice(functinBreak); 186 | 187 | output.push(apiRoute); 188 | } 189 | // output the routes 190 | console.table(output); 191 | }; 192 | 193 | 194 | apis.deleteRoute = async (apiName, method, route) => { 195 | // verify that method type is accurate 196 | method = verifyMethod(method); 197 | if (!method) return; 198 | 199 | // get api id 200 | const apiId = await getApiId(apiName); 201 | if (!apiId) return; 202 | 203 | const params = { 204 | ApiId: apiId 205 | }; 206 | 207 | // get methods and then the specific integration 208 | const routeKey = method.toUpperCase() + ' /' + route; 209 | const routes = await getApi.getRoutes(params); 210 | 211 | for (const item of routes.Items) { 212 | if (item.RouteKey === routeKey) { 213 | console.log(' Found matching integration\n'); 214 | params.IntegrationId = item.Target.slice(13); 215 | params.RouteId = item.RouteId; 216 | break; 217 | } 218 | } 219 | 220 | // get the information from the integration 221 | const integration = await getApi.getIntegration(params); 222 | const functinBreak = integration.IntegrationUri.indexOf('function:') + 9; 223 | const functionName = integration.IntegrationUri.slice(functinBreak); 224 | 225 | // delete the route and integration 226 | await deleteApi.deleteRoute(params); 227 | await deleteApi.deleteIntegration(params); 228 | 229 | // form the comparison permission 230 | if (route === '.') route = ''; 231 | let sourceArn = `arn:aws:execute-api:${AwsRegion}:${AwsAccount}:${apiId}/*/*/`; 232 | if (route) sourceArn = sourceArn + `${route}`; 233 | 234 | // remove the permissions from the function 235 | let {Policy} = await lambda.getPolicy(functionName); 236 | Policy = JSON.parse(Policy); 237 | 238 | let statementId = ''; 239 | for (const statement of Policy.Statement) { 240 | if(sourceArn === statement.Condition.ArnLike['AWS:SourceArn']) { 241 | statementId = statement.Sid; 242 | break; 243 | } 244 | } 245 | await lambda.removePermission(functionName, statementId); 246 | return; 247 | }; 248 | 249 | apis.deploy = async (apiName, stageName, options) => { 250 | const outputParams = {}; 251 | 252 | const params = { 253 | Name: apiName, 254 | StageName: stageName 255 | }; 256 | 257 | if (options.description) params.Description = options.description; 258 | 259 | // get api id 260 | params.ApiId = await getApiId(apiName); 261 | if (!params.ApiId) return; 262 | 263 | // add a deployment 264 | const createDeploymentResponse = await awsApi.createDeployment(params); 265 | if (!createDeploymentResponse) return; 266 | else { 267 | params.DeploymentId = createDeploymentResponse.DeploymentId; 268 | } 269 | 270 | // create a stage 271 | const createStageResponse = await awsApi.createStage(params); 272 | if (!createStageResponse) return; 273 | else { 274 | outputParams.StageName = createStageResponse.StageName; 275 | } 276 | 277 | // get the apiData 278 | const getApiResponse = await getApi.getApi(params); 279 | outputParams.ApiEndpoint = getApiResponse.ApiEndpoint; 280 | 281 | console.log(finished(`The API "${apiName}" has been deloyed. See: \n `)); 282 | console.log(code(` ${outputParams.ApiEndpoint}/${outputParams.StageName}/\n`)); 283 | 284 | }; 285 | 286 | export default apis; -------------------------------------------------------------------------------- /methods/commands/buckets.js: -------------------------------------------------------------------------------- 1 | import s3 from '../AWS/s3.js'; 2 | import { AwsBucket } from '../util/aws.js'; 3 | import { verifyBucket } from '../util/verifyAWS.js'; 4 | import { intro, starting, error, fail, finished, code } from '../util/chalkColors.js'; 5 | 6 | const buckets = async (s3bucket, options) => { 7 | // console.log(await s3.getBucketList()); 8 | if (options.delete) { 9 | if (s3bucket === AwsBucket) { 10 | console.log(fail('Cannot delete default bucket. Change default bucket before deleting')); 11 | return; 12 | } 13 | let data; 14 | if (options.bucket) data = await s3.deleteBucket(options.bucket); 15 | if (!options.bucket && s3bucket !== AwsBucket) data = await s3.deleteBucket(s3bucket); 16 | if (data) console.log(finished(` S3 bucket '${options.bucket || s3bucket}' deleted.`)); 17 | return; 18 | } 19 | if (options.list) console.log(await s3.getBucketList()); 20 | if (options.bucket) await verifyBucket(options.bucket, options.create); 21 | if (!options.bucket && s3bucket !== AwsBucket) await verifyBucket(s3bucket, options.create); 22 | }; 23 | 24 | export default buckets; -------------------------------------------------------------------------------- /methods/commands/functions.js: -------------------------------------------------------------------------------- 1 | import {verifyRole, verifyBucket} from '../util/verifyAWS.js'; 2 | 3 | import lambda from '../AWS/lambda.js'; 4 | import s3 from '../AWS/s3.js'; 5 | 6 | import {AwsBucket, AwsRole } from '../util/aws.js'; 7 | import archiver from '../util/archiver.js'; 8 | 9 | import { intro, starting, error, fail, finished, code } from '../util/chalkColors.js'; 10 | 11 | const lambdaFunctions = {}; 12 | 13 | lambdaFunctions.create = async (funcName, fileArr, options = {}) => { 14 | options.role ? await verifyRole((options.role || funcName || AwsRole), true) : await verifyRole(AwsRole, true); 15 | options.bucket ? await verifyBucket(options.bucket, true) : await verifyBucket(AwsBucket, true); 16 | 17 | let created; 18 | // alana create function1 functionfile1 -l layer1 -f layerfile1 layerfile2 19 | console.log(starting('Compressing files...')); 20 | const outputZip = await archiver.zipFiles(fileArr); 21 | console.log(starting('Sending files to s3...')); 22 | const response = await s3.sendFile(outputZip, options.bucket); 23 | console.log(starting('Sending files to AWS Lambda...')); 24 | if (response) created = await lambda.createFunction(outputZip, funcName, options); 25 | // console.log('response',response); 26 | if (created.$metadata.httpStatusCode < 300) console.log(finished('Request completed: AWS Lambda function created')); 27 | }; 28 | 29 | lambdaFunctions.list = async (options) => { 30 | if (options.function) { 31 | lambda.getFuncVersionList(options.function); 32 | return; 33 | } 34 | const list = await lambda.getFuncList(); 35 | console.table(list); 36 | }; 37 | 38 | lambdaFunctions.delete = async (funcName, qualifier) => { 39 | const response = await lambda.deleteFunction(funcName, qualifier); 40 | if (response.$metadata.httpStatusCode < 300) console.log(finished('Request complete: AWS Lambda function deleted')); 41 | }; 42 | 43 | lambdaFunctions.update = async (funcName, fileArr, options) => { 44 | // const outputZip = `${fileArr}.zip`; 45 | let updated; 46 | console.log(starting('Compressing updated files...')); 47 | const outputZip = await archiver.zipFiles(fileArr); 48 | // await archiver.zipFiles(fileArr); 49 | console.log(starting('Sending files to s3...')); 50 | const response = await s3.sendFile(outputZip); 51 | console.log(starting('Sending files to AWS Lambda...')); 52 | if (response) updated = await lambda.updateFunction(outputZip, funcName, options); 53 | if (updated.$metadata.httpStatusCode < 300) console.log(finished('Request complete: AWS Lambda function updated')); 54 | }; 55 | 56 | lambdaFunctions.invoke = async (funcName, params, options) => { 57 | lambda.invoke(funcName, params, options); 58 | console.log(finished('Request complete: Lambda function invoked')); 59 | }; 60 | 61 | export default lambdaFunctions; -------------------------------------------------------------------------------- /methods/commands/layers.js: -------------------------------------------------------------------------------- 1 | import lambda from '../AWS/lambda.js'; 2 | import s3 from '../AWS/s3.js'; 3 | 4 | import archiver from '../util/archiver.js'; 5 | 6 | import { intro, starting, error, fail, finished, code } from '../util/chalkColors.js'; 7 | 8 | const layers = {}; 9 | 10 | layers.create = async (layerName, fileArr) => { 11 | 12 | // if(!fileArr && !layerName){ 13 | // console.log(error('both fileArr and layerName are required fields')); 14 | // return; 15 | // } 16 | console.log(starting('Compressing layer files...')); 17 | const outputZip = await archiver.zipFiles(fileArr, layerName, true); 18 | 19 | console.log(starting('Sending files to S3...')); 20 | await s3.sendFile(outputZip); 21 | 22 | console.log(starting('Sending files to AWS Lambda...')); 23 | const response = await lambda.createLambdaLayer(layerName, outputZip); 24 | if (response.$metadata.httpStatusCode < 300) { 25 | console.log(finished('Request complete: Lambda layers created')); 26 | } 27 | else { 28 | console.log(error('Error with sending request to AWS Lambda')); 29 | } 30 | }; 31 | 32 | layers.addLayersToFunc = async(funcName, options) => { 33 | 34 | const layerArr = [{layerName: options.layerName, layerVersion: options.layerVersion}]; 35 | 36 | if(!funcName || !layerArr){ 37 | console.log(error('funcName, layerName, and layerVersion are required fields')); 38 | return; 39 | } 40 | console.log(starting('Sending request to AWS Lambda...')); 41 | const response = await lambda.addLayerToFunc(funcName, layerArr); 42 | if (response.$metadata.httpStatusCode < 300) { 43 | console.log(finished('Request complete: Lambda layers added to function')); 44 | } 45 | else { 46 | console.log(error('Error with sending request to AWS Lambda')); 47 | } 48 | 49 | }; 50 | 51 | export default layers; -------------------------------------------------------------------------------- /methods/commands/roles.js: -------------------------------------------------------------------------------- 1 | import iam from '../AWS/iam.js'; 2 | import { AwsRole } from '../util/aws.js'; 3 | import { verifyRole } from '../util/verifyAWS.js'; 4 | import { intro, starting, error, fail, finished, code } from '..//util/chalkColors.js'; 5 | 6 | const roles = async (role, options) => { 7 | if (options.delete) { 8 | if (role === AwsRole) { 9 | console.log(fail('Cannot delete default role. Change default role before deleting')); 10 | return; 11 | } 12 | let data; 13 | if (options.role) data = await iam.deleteRole(options.role); 14 | if (!options.role && role !== AwsRole) data = await iam.deleteRole(role); 15 | if (data) console.log(finished(` AWS role '${options.role || role}' deleted.`)); 16 | return; 17 | } 18 | if (options.list) console.log(await iam.getRoleList()); 19 | if (options.role) await verifyRole(options.role, options.create); 20 | if (!options.role && role !== AwsRole) await verifyRole(role, options.create); 21 | }; 22 | 23 | export default roles; -------------------------------------------------------------------------------- /methods/util/archiver.js: -------------------------------------------------------------------------------- 1 | import path, {dirname} from 'path'; 2 | import fs, { ReadStream } from 'fs'; 3 | import archiver from 'archiver'; 4 | import {finished as finishedStreamWriting} from 'stream/promises'; 5 | 6 | import { starting, code, error, finished } from './chalkColors.js'; 7 | 8 | import dotenv from 'dotenv'; 9 | dotenv.config(); 10 | 11 | const FOLDER = process.env.FOLDER; 12 | 13 | const archiveZip = {}; 14 | 15 | 16 | //should zip files and folders 17 | archiveZip.zipFiles = async (fileArr, outputFileName = 'function', layer = false) => { 18 | 19 | // breaks up fileArr into the first file and the rest 20 | let [index, ...args] = fileArr; 21 | // if lambda layer 22 | if (layer) args = fileArr; 23 | 24 | console.log(args); 25 | 26 | console.log(starting(`Adding the following files/directories to the output zip file "${outputFileName}.zip" : `)); 27 | if (!layer) console.log(code(` ${index}`)); 28 | 29 | // create a file to stream archive data to. 30 | const output = fs.createWriteStream(`${outputFileName}.zip`); 31 | 32 | const archive = archiver('zip'); 33 | 34 | archive.on('warning', function(err) { 35 | if (err.code === 'ENOENT') { 36 | // log warning 37 | } else { 38 | // throw error 39 | throw err; 40 | } 41 | }); 42 | 43 | // if zipping a lambda function, adds the first file as index.js 44 | const stream = fs.createReadStream(path.join(`${FOLDER}`) + '/' + index); 45 | if (!layer) { 46 | const stats = fs.statSync(path.join(`${FOLDER}`) + '/' + index); 47 | if (stats.isDirectory()) archive.directory(`${FOLDER}/${index}/`, index); 48 | else archive.file(path.join(`${FOLDER}`) + '/' + index, {name: 'index.js'}); 49 | } 50 | 51 | //iterate over the remaining file names in fileArr and add them as their original names 52 | for (const file of args) { 53 | console.log(code(` ${file}`)); 54 | const stats = fs.statSync(path.join(`${FOLDER}`) + '/' + file); 55 | if (stats.isDirectory()) archive.directory(`${FOLDER}/${file}/`, file); 56 | else { 57 | if (layer) archive.file(path.join(`${FOLDER}`) + '/' + file, {name: `nodejs/node_modules/${file}`}); 58 | else archive.file(path.join(`${FOLDER}`) + '/' + file, {name: `${file}`}); 59 | } 60 | } 61 | // pipe archive data to the file 62 | archive.pipe(output); 63 | 64 | // finalize the archive (ie we are done appending files but streams have to finish yet) 65 | await archive.finalize(); 66 | 67 | const data = await finishedStreamWriting(output, {}, (err) => { 68 | if(err){ 69 | console.log(error('Error while zipping up file: ', err)); 70 | } else{ 71 | console.log('Output finished writing'); 72 | } 73 | }); 74 | 75 | return `${outputFileName}.zip`; 76 | }; 77 | 78 | 79 | export default archiveZip; -------------------------------------------------------------------------------- /methods/util/aws.js: -------------------------------------------------------------------------------- 1 | // set up .env file accessibility 2 | import dotenv from 'dotenv'; 3 | dotenv.config(); 4 | 5 | // root user credentials 6 | const credentials = { 7 | accessKeyId: process.env.AWS_ACCESS_KEY_ID, 8 | secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, 9 | }; 10 | 11 | // set up AWS region 12 | const AwsRegion = process.env.AWS_REGION; 13 | 14 | const AwsParams = { 15 | 'region': AwsRegion, 16 | 'credentials': credentials, 17 | }; 18 | 19 | // the basic policy needed from AWS in order to create a role for lambda 20 | const BasicPolicy = { 21 | 'Version': '2012-10-17', 22 | 'Statement': [ 23 | { 24 | 'Effect': 'Allow', 25 | Principal: { 26 | Service: 'lambda.amazonaws.com', 27 | }, 28 | 'Action': 'sts:AssumeRole', 29 | } 30 | ] 31 | }; 32 | 33 | // default ARNs 34 | const LambdaBasicARN = 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'; 35 | 36 | //gets the .env bucket name and awsRole 37 | const AwsBucket = process.env.S3BUCKETNAME; 38 | const AwsRole = process.env.ROLENAME; 39 | const AwsAccount = process.env.AWS_ACCOUNT; 40 | 41 | 42 | 43 | export {AwsAccount, AwsParams, AwsBucket, AwsRegion, AwsRole, BasicPolicy, LambdaBasicARN}; 44 | -------------------------------------------------------------------------------- /methods/util/chalkColors.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | export const intro = chalk.bold.yellow; 4 | export const code = chalk.bold; 5 | export const starting = chalk.bold; 6 | export const error = chalk.bold.red; 7 | export const fail = chalk.bold.red; 8 | export const finished = chalk.green; 9 | export const warning = chalk.bold.yellowBright; 10 | -------------------------------------------------------------------------------- /methods/util/default.js: -------------------------------------------------------------------------------- 1 | const random = (length = 6) => { 2 | return Math.random().toString(16).slice(2, length); 3 | }; 4 | 5 | const startingRegion = 'us-east-1'; 6 | const startingRole = 'defaultLambdaRole'; 7 | const startingBucket = 'defaultbucket-' + random(); 8 | const startingFolder = ''; 9 | 10 | export { startingRegion, startingRole, startingBucket, startingFolder}; -------------------------------------------------------------------------------- /methods/util/generateEnv.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'fs'; 3 | import {writeFile, appendFile} from 'fs/promises'; 4 | 5 | import { intro, starting, error, fail, finished, code } from './chalkColors.js'; 6 | import {checkConnection} from './verifyAWS.js'; 7 | import {startingBucket, startingRegion, startingRole, startingFolder} from './default.js'; 8 | 9 | 10 | async function init (id, key, account, region = startingRegion, role = startingRole, bucket = startingBucket, directory = startingFolder, update) { 11 | // check if .gitignore exists 12 | if (!fs.existsSync(path.resolve('./.gitignore'))) { 13 | 14 | // if it doesn't exist, create it with .env 15 | await writeFile('./.gitignore', '.env') 16 | .catch(err => { 17 | console.log(error(`Error in creating ./.gitignore : ${err.message}`)); 18 | return; 19 | }); 20 | console.log('.gitignore Created'); 21 | } 22 | 23 | else { 24 | // if it does exist, read gitignore 25 | fs.readFile('./.gitignore', async (err, data) => { 26 | 27 | // check for .env and add if it doesn't 28 | if (!data.includes('.env')) { 29 | 30 | //append onto gitignore 31 | await appendFile('./.gitignore', '.env') 32 | .catch(err => { 33 | console.log(error(`Error in appending to ./.gitignore : ${err.message}`)); 34 | }); 35 | console.log('.env Added'); 36 | } 37 | }); 38 | } 39 | 40 | const accountId = await checkConnection(id, key, account, region); 41 | if (!accountId) return; 42 | 43 | // remove slash if folder contains a slash as last char 44 | if (directory[directory.length - 1] === '/') directory = directory.slice(0, directory.length - 1); 45 | 46 | // create the aws credentials string 47 | const awsID = `AWS_ACCESS_KEY_ID=${id}\n`; 48 | const awsKey = `AWS_SECRET_ACCESS_KEY=${key}\n`; 49 | const awsAccount = `AWS_ACCOUNT=${accountId}\n`; 50 | const awsRegion = `AWS_REGION=${region}\n`; 51 | const s3Bucket = `S3BUCKETNAME=${bucket}\n`; 52 | const awsRole = `ROLENAME=${role}\n`; 53 | const folder = `FOLDER=${directory}\n`; 54 | 55 | // check if .env exists 56 | if (!fs.existsSync(path.resolve('./.env'))) { 57 | //if it doesn't exist, create it with .env 58 | await writeFile('./.env', awsID + awsKey + awsAccount + awsRegion + s3Bucket + awsRole + folder) 59 | .catch(err => { 60 | console.log(error(`Error writing to the file ./.env : ${err.message}`)); 61 | return; 62 | }); 63 | console.log('.env Created'); 64 | console.log(finished('AWS configuration finished!')); 65 | } 66 | else { 67 | 68 | //if it does exist, check for check for aws credentials 69 | fs.readFile('./.env', 'utf8', async (err, data) => { 70 | 71 | // if there is an option to update 72 | if (update) { 73 | // split up the data into an array 74 | const data_array = data.split('\n'); 75 | // use a helper function to determine the data to delete 76 | const textLine = (text) => { 77 | for (let i = 0; i < data_array.length; i++) { 78 | if (data_array[i] && data_array[i].match(text)) { 79 | return i; 80 | } 81 | } 82 | }; 83 | // delete the three main arguments 84 | delete data_array[textLine('AWS_ACCESS_KEY_ID')]; 85 | delete data_array[textLine('AWS_SECRET_ACCESS_KEY')]; 86 | delete data_array[textLine('AWS_ACCOUNT')]; 87 | // if there are options 88 | if (region !== startingRegion) delete data_array[textLine('AWS_REGION')]; 89 | if (role && role !== startingRole) delete data_array[textLine('ROLENAME')]; 90 | if (bucket && bucket !== startingBucket) delete data_array[textLine('S3BUCKETNAME')]; 91 | 92 | // turn back into string while ignoring whitespaces 93 | data = ''; 94 | data_array.forEach(el => { 95 | if (el !== '') data += el + '\n'; 96 | }); 97 | } 98 | 99 | // add all updated parameters 100 | if (!data.includes('AWS_ACCESS_KEY_ID')) { 101 | data += awsID; 102 | console.log('AWS Access Key ID Added'); 103 | } 104 | if (!data.includes('AWS_SECRET_ACCESS_KEY')) { 105 | data += awsKey; 106 | console.log('AWS Secret Access Key Added'); 107 | } 108 | if (!data.includes('AWS_ACCOUNT')) { 109 | data += awsAccount; 110 | console.log('AWS Account Added'); 111 | } 112 | if (!data.includes('AWS_REGION')) { 113 | data += awsRegion; 114 | console.log('AWS Region Added'); 115 | } 116 | if (!data.includes('ROLENAME')) { 117 | data += awsRole; 118 | console.log('Role Name Added'); 119 | } 120 | if (!data.includes('S3BUCKETNAME')) { 121 | data += s3Bucket; 122 | console.log('S3 Bucket Name Added'); 123 | } 124 | if (!data.includes('FOLDER')) { 125 | data += folder; 126 | console.log('Folder Name Added'); 127 | } 128 | 129 | //write it back to the .env file 130 | await writeFile('./.env', data) 131 | .catch(err => { 132 | console.log(error(`Error in modifying ./.env : ${err.message}`)); 133 | return; 134 | }); 135 | 136 | console.log('.env finished!'); 137 | console.log(finished('AWS configuration finished!')); 138 | }); 139 | } 140 | } 141 | 142 | export default init; -------------------------------------------------------------------------------- /methods/util/verifyAWS.js: -------------------------------------------------------------------------------- 1 | import { STSClient, GetAccessKeyInfoCommand } from "@aws-sdk/client-sts"; 2 | 3 | import iam from '../AWS/iam.js'; 4 | import s3 from '../AWS/s3.js'; 5 | import lambda from "../AWS/lambda.js"; 6 | 7 | import {starting, finished, warning, fail} from './chalkColors.js'; 8 | 9 | // verifies that the role exists and create if create is true 10 | async function verifyRole(roleName, create = false) { 11 | const verifyResult = await iam.verifyRole(roleName); 12 | verifyResult ? console.log(finished(' Role exists\n')) : console.log(fail(' Role doesn\'t exist\n')); 13 | if (create && !verifyResult) await iam.createRole(roleName); 14 | } 15 | 16 | // verifies that the bucket exists and create if otherwise 17 | async function verifyBucket(bucket, create = false) { 18 | if (bucket !== bucket.toLowerCase()) { 19 | console.log(fail(' AWS S3 buckets must be in lower case only')); 20 | return; 21 | } 22 | const verifyResult = await s3.verifyBucket(bucket); 23 | verifyResult ? console.log(finished(' Bucket exists\n')) : console.log(fail(' Bucket doesn\'t exist\n')); 24 | if (create && !verifyResult) await s3.createBucket(bucket); 25 | } 26 | 27 | // verifies that the function is a valid function 28 | async function verifyFunction(funcName) { 29 | const functionObj = await lambda.getFuncList(); 30 | const functions = Object.keys(functionObj); 31 | const result = functions.includes(funcName); 32 | result ? console.log('Verified function name') : console.log(fail('Cannot find function name.')); 33 | return result; 34 | } 35 | 36 | async function checkConnection(id, key, account, region) { 37 | const credentials = { 38 | accessKeyId: id, 39 | secretAccessKey: key 40 | }; 41 | 42 | const awsParams = { 43 | 'region': region, 44 | 'credentials': credentials, 45 | }; 46 | 47 | const stsClient = new STSClient(awsParams); 48 | 49 | console.log(starting('Verifying AWS credentials...')); 50 | 51 | const data = await stsClient.send(new GetAccessKeyInfoCommand({AccessKeyId: id})) 52 | .catch(error => { 53 | // error handling. 54 | console.log(fail('\nError in verifying AWS credentials')); 55 | console.log(error.message); 56 | console.log(fail('Please ensure that the ID/Key from this specific user has admin rights.')); 57 | console.log('These privileges are required to properly create and execute commands in AWS.'); 58 | console.log('If you do not wish to give this specific user admin privileges, please ensure the user has S3 and Lambda read/write permissions.'); 59 | return; 60 | }); 61 | if (!data) { 62 | if (!account) { 63 | console.log(fail('No account number was given')); 64 | console.log('Re-run initialization or see alanajs documentation on how to properly create the .ENV file.'); 65 | return false; 66 | } 67 | else { 68 | console.log(warning('The .ENV file will be created with the account number provided.')); 69 | console.log(warning('The account number could not be verified, and problems may occur if account number is incorrect')); 70 | return account; 71 | } 72 | } 73 | if (account && data.Account !== account) { 74 | console.log(fail('\nError in matching account read from AWS and the account entered. Please verify if the account number is correct.')); 75 | return; 76 | } 77 | console.log(finished(' AWS Credentials verified.')); 78 | return data.Account; 79 | } 80 | 81 | export {verifyRole, verifyBucket, verifyFunction, checkConnection}; -------------------------------------------------------------------------------- /npm_package/index.js: -------------------------------------------------------------------------------- 1 | import lambda from '../methods/AWS/lambda.js'; 2 | import s3 from '../methods/AWS/s3.js'; 3 | import zip from '../methods/util/archiver.js'; 4 | import lambdaFunctions from '../methods/commands/functions.js'; 5 | import layers from '../methods/commands/layers.js'; 6 | import API from '../methods/AWS/gatewayv2'; 7 | 8 | const alana = {}; 9 | 10 | /** 11 | * @FunctionName: getFuncList 12 | * @Description: Displays table of lambda functions 13 | */ 14 | alana.getFuncList = async () => { 15 | console.log('alana.getFuncList invoked'); 16 | const functionList = await lambda.getFuncList(); 17 | console.table(functionList); 18 | console.log('Finished getting Lambda function list'); 19 | return functionList; 20 | }; 21 | 22 | /** 23 | * @FunctionName: getFuncVersions 24 | * @Description: Displays table of function versions 25 | * @input: string that contains function name 26 | */ 27 | alana.getFuncVersions = async (funcName) => { 28 | console.log('alana.getFuncVersions invoked'); 29 | const versionList = await lambda.getFuncVersionList(funcName); 30 | console.table(versionList); 31 | console.log('Finished getting Lambda function versions'); 32 | return versionList; 33 | }; 34 | 35 | /** 36 | * @FunctionName: createFunction 37 | * @Description: creates AWS Lambda function 38 | * @input: params object which includes array of file names and name of function :options object 39 | */ 40 | alana.createFunction = async (params, options = {}) => { 41 | const {fileArr, funcName} = params; 42 | console.log('alana.createFunction invoked'); 43 | await lambdaFunctions.create(params.funcName, params.fileArr, options); 44 | console.log('Lambda function has been created'); 45 | }; 46 | 47 | /** 48 | * @FunctionName: updateFunction 49 | * @Description: updates AWS Lambda function 50 | * @input: params object which includes array of file names and name of function to be updated 51 | */ 52 | alana.updateFunction = async (params) => { 53 | const {fileArr, funcName} = params; 54 | console.log('alana.updateFunction invoked'); 55 | const zipFile = await zip.zipFiles(fileArr); 56 | await s3.sendFile(zipFile); 57 | await lambda.updateFunction(zipFile, funcName); 58 | console.log('Lambda function has been updated'); 59 | }; 60 | 61 | /** 62 | * @FunctionName: deleteFunction 63 | * @Description: deletes AWS Lambda function 64 | * @input: string which contains function name, optional qualifier 65 | */ 66 | alana.deleteFunction = async (funcName, qualifier) => { 67 | console.log('alana.deleteFunction invoked'); 68 | await lambda.deleteFunction(funcName, qualifier); 69 | console.log('Lambda function has been deleted'); 70 | }; 71 | 72 | /** 73 | * @FunctionName: createLambdaLayer 74 | * @Description: creates AWS Lambda layer 75 | * @input: params object that contains array of files and layer name 76 | */ 77 | alana.createLambdaLayer = async (params) => { 78 | const {fileArr, layerName} = params; 79 | await layers.create(layerName, fileArr); 80 | }; 81 | 82 | /** 83 | * @FunctionName: createAlias 84 | * @Description: creates Alias for Lambda functions 85 | * @input: params object which includes function name and version 86 | */ 87 | alana.createAlias = async (params, aliasName) => { 88 | const {funcName, version} = params; 89 | console.log('alana.createAlias invoked'); 90 | await lambda.createAlias(funcName, version, aliasName); 91 | console.log('Lambda Alias function has been created'); 92 | }; 93 | 94 | /** 95 | * @FunctionName: updateAlias 96 | * @Description: updates Alias for Lambda functions 97 | * @input: params object which includes function name and version 98 | */ 99 | alana.updateAlias = async (params, aliasName) => { 100 | const {funcName, version} = params; 101 | console.log('alana.updateAlias invoked'); 102 | await lambda.updateAlias(funcName, version, aliasName); 103 | console.log('Lambda Alias function has been updated'); 104 | }; 105 | 106 | /** 107 | * @FunctionName: deleteAlias 108 | * @Description: deletes Alias for Lambda functions 109 | * @input: params object which includes function name 110 | */ 111 | alana.deleteAlias = async (params, aliasName) => { 112 | const {funcName} = params; 113 | console.log('alana.deleteAlias invoked'); 114 | await lambda.deleteAlias(funcName, aliasName); 115 | console.log('Lambda Alias function has been deleted'); 116 | }; 117 | 118 | /** 119 | * @FunctionName: invoke 120 | * @Description: invokes Lambda function 121 | * @input: funcName - string that includes name of function : params - object 122 | * @output: result of invoked function 123 | */ 124 | alana.invoke = async (funcName, params) => { 125 | console.log('alana.invoke invoked'); 126 | await lambda.invoke(funcName); 127 | console.log('Lambda function has been invoked'); 128 | }; 129 | 130 | /** 131 | * @FunctionName: addLayerToFunc 132 | * @Description: adds Lambda layer to Lambda function 133 | * @input: funcName - string that includes name of function: layerArr - array of objects which include name of files and name of layer 134 | */ 135 | alana.addLayerToFunction = async (funcName, layer) => { 136 | console.log('alana.addLayerToFunc invoked'); 137 | await layers.addLayersToFunc(funcName, layer); 138 | // await lambda.addLayerToFunc(funcName, layerArr); 139 | console.log('Lambda layer added to function'); 140 | }; 141 | 142 | export default alana; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alanajs", 3 | "keywords": [ 4 | "AWS", 5 | "Lambda", 6 | "AWS-Lambda", 7 | "amazon web services", 8 | "cli", 9 | "api", 10 | "api gateway", 11 | "serverless", 12 | "microservices", 13 | "AWS-SDK" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/oslabs-beta/alanajs" 18 | }, 19 | "author": "Tin Khin, Eugene Lee, Amy Liang, Jae Hyun Ha", 20 | "version": "0.9.0", 21 | "license": "MIT", 22 | "description": "A free, open source npm package that makes it easy to create and deploy Lambda functions, incorporate microservices in your application, and build on API gateway.", 23 | "homepage": "https://www.alanajs.com/", 24 | "main": "npm_package/index.js", 25 | "bin": { 26 | "alana": "./bin/cli.js" 27 | }, 28 | "scripts": { 29 | "test": "jest" 30 | }, 31 | "type": "module", 32 | "dependencies": { 33 | "@aws-sdk/client-apigatewayv2": "^3.48.0", 34 | "@aws-sdk/client-iam": "^3.47.0", 35 | "@aws-sdk/client-lambda": "^3.46.0", 36 | "@aws-sdk/client-s3": "^3.46.0", 37 | "@aws-sdk/client-sts": "^3.49.0", 38 | "archiver": "^5.3.0", 39 | "chalk": "^5.0.0", 40 | "commander": "^8.3.0", 41 | "dotenv": "^11.0.0", 42 | "fs": "^0.0.1-security", 43 | "path": "^0.12.7" 44 | }, 45 | "devDependencies": { 46 | "aws-sdk-client-mock": "^0.5.6", 47 | "eslint": "^8.6.0", 48 | "eslint-plugin-react": "^7.28.0", 49 | "eslint-plugin-react-hooks": "^4.3.0" 50 | } 51 | } 52 | --------------------------------------------------------------------------------