├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── config.json ├── jest.config.js ├── lib ├── index.d.ts ├── index.js └── index.js.map ├── package-lock.json ├── package.json ├── src ├── index.spec.ts ├── index.ts └── issue#16.spec.ts ├── testdata └── smiley.png └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:@typescript-eslint/recommended" 9 | ], 10 | "parser": "@typescript-eslint/parser", 11 | "parserOptions": { 12 | "ecmaVersion": 12, 13 | "sourceType": "module" 14 | }, 15 | "plugins": [ 16 | "@typescript-eslint" 17 | ], 18 | "rules": { 19 | "@typescript-eslint/no-unused-vars": "off", 20 | "@typescript-eslint/no-explicit-any": "off" 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | my_config.json 30 | my_config2.json 31 | 32 | .DS_Store 33 | npm-debug.log 34 | dist 35 | typings 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Matthias Ludwig (mludwig@quobject.io) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aws-cli-js 2 | A node.js wrapper for the [aws-cli](http://aws.amazon.com/documentation/cli/) command line interface 3 | 4 | [![NPM version][npm-image]][npm-url] 5 | [![NPM downloads][downloads-image]][downloads-url] 6 | 7 | ## Installation 8 | 9 | ### Warning 10 | 11 | Code uses exec = child_process.exec; https://github.com/Quobject/docker-cli-js/issues/22 12 | 13 | 14 | ### Step 1: Prerequisites 15 | 16 | The aws command line interface must be installed and accessible in the path 17 | 18 | ### Step 2: Installation 19 | 20 | npm install aws-cli-js 21 | 22 | Then: 23 | 24 | ```js 25 | var awsCli = require('aws-cli-js'); 26 | var Options = awsCli.Options; 27 | var Aws = awsCli.Aws; 28 | ``` 29 | 30 | ## Usage 31 | 32 | With promise 33 | 34 | ```js 35 | var options = new Options( 36 | /* accessKey */ 'your key', 37 | /* secretKey */ 'your key2', 38 | /* sessionToken */ 'your token', 39 | /* currentWorkingDirectory */ null, 40 | /* cliPath */ 'aws' 41 | ); 42 | 43 | var aws = new Aws(options); 44 | 45 | aws.command('iam list-users').then(function (data) { 46 | console.log('data = ', data); 47 | }); 48 | 49 | //data = { 50 | // command: 'aws iam list-users ', 51 | // raw: '{\\n \\"Users\\": [\\n {\\n \\"UserName\\": \\"developer\\", \\n \\"PasswordLastUsed\\": \\"2015-10-03T17:58:49Z\\", \\n \\"CreateDate\\": \\"2015-06-03T07:37:25Z\\", \\n \\"UserId\\": \\"AIDAJBXXXXXXXXXXXXXXXXX\\", \\n \\"Path\\": \\"/\\", \\n \\"Arn\\": \\"arn:aws:iam::03XXXXXXXXX:user/developer\\"\\n }\\n ]\\n}\\n', 52 | // object: 53 | // { 54 | // Users: 55 | // [{ 56 | // UserName: 'developer', 57 | // PasswordLastUsed: '2015-10-03T17:58:49Z', 58 | // CreateDate: '2015-06-03T07:37:25Z', 59 | // UserId: 'AIDAJBXXXXXXXXXXXXXXXXX', 60 | // Path: '/', 61 | // Arn: 'arn:aws:iam::03XXXXXXXXX:user/developer' 62 | // }] 63 | // } 64 | //} 65 | 66 | ``` 67 | 68 | With callback: 69 | 70 | ```js 71 | 72 | aws.command('iam list-users', function (err, data) { 73 | console.log('data = ', data); 74 | }); 75 | 76 | //data = { 77 | // command: 'aws iam list-users ', 78 | // raw: '["{\\n \\"Users\\": [\\n {\\n \\"UserName\\": \\"developer\\", \\n \\"PasswordLastUsed\\": \\"2015-10-03T17:58:49Z\\", \\n \\"CreateDate\\": \\"2015-06-03T07:37:25Z\\", \\n \\"UserId\\": \\"AIDAJBXXXXXXXXXXXXXXXXX\\", \\n \\"Path\\": \\"/\\", \\n \\"Arn\\": \\"arn:aws:iam::03XXXXXXXXX:user/developer\\"\\n }\\n ]\\n}\\n",""]', 79 | // object: 80 | // { 81 | // Users: 82 | // [{ 83 | // UserName: 'developer', 84 | // PasswordLastUsed: '2015-10-03T17:58:49Z', 85 | // CreateDate: '2015-06-03T07:37:25Z', 86 | // UserId: 'AIDAJBXXXXXXXXXXXXXXXXX', 87 | // Path: '/', 88 | // Arn: 'arn:aws:iam::03XXXXXXXXX:user/developer' 89 | // }] 90 | // } 91 | //} 92 | 93 | ``` 94 | 95 | Typescript 96 | 97 | ```js 98 | import { Aws, Options } from 'aws-cli-js'; 99 | 100 | const options = new Options( 101 | /* accessKey */ 'your key', 102 | /* secretKey */ 'your key2', 103 | /* sessionToken */ 'your token', 104 | /* currentWorkingDirectory */ null 105 | ); 106 | 107 | 108 | const aws = new Aws(options); 109 | 110 | return aws.command('iam list-users').then(function (data) { 111 | console.log('data = ', data); 112 | }); 113 | ``` 114 | 115 | 116 | 117 | * describe-instances 118 | 119 | ```js 120 | awsCli.command('ec2 describe-instances --instance-ids i-789b3ba7').then(function (data) { 121 | console.log('data = ', data); 122 | }); 123 | 124 | 125 | //data = { command: 'aws ec2 describe-instances --instance-ids i-789b3ba7 ', 126 | // raw: '{\\n \\"Reservations\\": [\\n {\\n \\"OwnerId\\": \\"031641171132\\", \\n \\"ReservationId\\": \\"r-a48ad878\\", \\n \\"Groups\\": [], \\n \\"Instances\\": [\\n {\\n 127 | // \\"Monitoring\\": {\\n \\"State\\": \\"disabled\\"\\n }, \\n 128 | // \\"PublicDnsName\\": \\"ec2-52-64-166-221.ap-southeast-2.compute.amazonaws.com\\", \\n \\"State\\": {\\n 129 | // ... 130 | 131 | ``` 132 | 133 | ## License 134 | 135 | MIT 136 | 137 | [npm-image]: https://img.shields.io/npm/v/aws-cli-js.svg?style=flat 138 | [npm-url]: https://npmjs.org/package/aws-cli-js 139 | [downloads-image]: https://img.shields.io/npm/dm/aws-cli-js.svg?style=flat 140 | [downloads-url]: https://npmjs.org/package/aws-cli-js 141 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "accessKeyId": "keyid", 3 | "secretAccessKey": "secret", 4 | "sessionToken": "token" 5 | } 6 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/src/'], 3 | transform: { 4 | '^.+\\.tsx?$': 'ts-jest' 5 | }, 6 | testRegex: '(/__test__/.*|(\\.|/)(test|spec))\\.[jt]sx?$', 7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 8 | verbose: true, 9 | collectCoverage: false, 10 | collectCoverageFrom: ['/src/**/*.ts'] 11 | } -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | export declare class Aws { 2 | private options; 3 | constructor(options?: IOptions); 4 | command(command: string, callback?: (err: any, data: any) => void): Promise; 5 | } 6 | export interface IOptions { 7 | accessKey?: string; 8 | secretKey?: string; 9 | sessionToken?: string; 10 | currentWorkingDirectory?: string; 11 | cliPath: string; 12 | } 13 | export declare class Options implements IOptions { 14 | accessKey?: string | undefined; 15 | secretKey?: string | undefined; 16 | sessionToken?: string | undefined; 17 | currentWorkingDirectory?: string | undefined; 18 | cliPath: string; 19 | constructor(accessKey?: string | undefined, secretKey?: string | undefined, sessionToken?: string | undefined, currentWorkingDirectory?: string | undefined, cliPath?: string); 20 | } 21 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); 5 | }) : (function(o, m, k, k2) { 6 | if (k2 === undefined) k2 = k; 7 | o[k2] = m[k]; 8 | })); 9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 10 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 11 | }) : function(o, v) { 12 | o["default"] = v; 13 | }); 14 | var __importStar = (this && this.__importStar) || function (mod) { 15 | if (mod && mod.__esModule) return mod; 16 | var result = {}; 17 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 18 | __setModuleDefault(result, mod); 19 | return result; 20 | }; 21 | var __importDefault = (this && this.__importDefault) || function (mod) { 22 | return (mod && mod.__esModule) ? mod : { "default": mod }; 23 | }; 24 | Object.defineProperty(exports, "__esModule", { value: true }); 25 | exports.Options = exports.Aws = void 0; 26 | const child_process = __importStar(require("child_process")); 27 | const nodeify_ts_1 = __importDefault(require("nodeify-ts")); 28 | const execFile = child_process.execFile; 29 | const exec = child_process.exec; 30 | const extractResult = (result) => { 31 | try { 32 | result.object = JSON.parse(result.raw); 33 | } 34 | catch (e) { 35 | result.object = e; 36 | } 37 | return result; 38 | }; 39 | class Aws { 40 | constructor(options = { 41 | accessKey: undefined, 42 | currentWorkingDirectory: undefined, 43 | secretKey: undefined, 44 | cliPath: 'aws' 45 | }) { 46 | this.options = options; 47 | } 48 | command(command, callback) { 49 | const execCommand = `${this.options.cliPath} ${command}`; 50 | const env_vars = ('HOME PATH AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY ' + 51 | 'AWS_SESSION_TOKEN AWS_DEFAULT_REGION ' + 52 | 'AWS_PROFILE AWS_DEFAULT_PROFILE AWS_CONFIG_FILE').split(' '); 53 | const env = env_vars.reduce((result, value) => { 54 | if (process.env[value]) { 55 | result[value] = process.env[value]; 56 | } 57 | return result; 58 | }, {}); 59 | env['DEBUG'] = ''; 60 | if (this.options.accessKey) { 61 | env['AWS_ACCESS_KEY_ID'] = this.options.accessKey; 62 | } 63 | if (this.options.secretKey) { 64 | env['AWS_SECRET_ACCESS_KEY'] = this.options.secretKey; 65 | } 66 | if (this.options.sessionToken) { 67 | env['AWS_SESSION_TOKEN'] = this.options.sessionToken; 68 | } 69 | const execOptions = { 70 | cwd: this.options.currentWorkingDirectory, 71 | env: env, 72 | maxBuffer: 200 * 1024 * 1024, 73 | }; 74 | const promise = Promise.resolve().then(() => { 75 | return new Promise((resolve, reject) => { 76 | if (command.indexOf('"') < 0) { 77 | let cmd = [...command.split(' ')]; 78 | cmd = cmd.filter(v => v.length > 0); 79 | //console.log('execFile'); 80 | execFile(this.options.cliPath, cmd, execOptions, (error, stdout, stderr) => { 81 | if (error) { 82 | const message = `error: '${error}' stdout = '${stdout}' stderr = '${stderr}'`; 83 | //console.error(message); 84 | reject(message); 85 | } 86 | //console.log(`stdout: ${stdout}`); 87 | resolve({ stderr: stderr, stdout: stdout }); 88 | }); 89 | } 90 | else { 91 | //console.log('exec'); 92 | exec(execCommand, execOptions, (error, stdout, stderr) => { 93 | if (error) { 94 | const message = `error: '${error}' stdout = '${stdout}' stderr = '${stderr}'`; 95 | //console.error(message); 96 | reject(message); 97 | } 98 | //console.log(`stdout: ${stdout}`); 99 | resolve({ stderr: stderr, stdout: stdout }); 100 | }); 101 | } 102 | }); 103 | }).then((data) => { 104 | const result = { 105 | command: execCommand, 106 | error: data.stderr, 107 | object: null, 108 | raw: data.stdout, 109 | }; 110 | return extractResult(result); 111 | }); 112 | return nodeify_ts_1.default(promise, callback); 113 | } 114 | } 115 | exports.Aws = Aws; 116 | class Options { 117 | constructor(accessKey, secretKey, sessionToken, currentWorkingDirectory, cliPath = 'aws') { 118 | this.accessKey = accessKey; 119 | this.secretKey = secretKey; 120 | this.sessionToken = sessionToken; 121 | this.currentWorkingDirectory = currentWorkingDirectory; 122 | this.cliPath = cliPath; 123 | } 124 | } 125 | exports.Options = Options; 126 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /lib/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-cli-js", 3 | "version": "2.2.3", 4 | "description": "A node.js wrapper for the aws command line interface", 5 | "main": "lib/index.js", 6 | "types": "lib/index.d.ts", 7 | "files": [ 8 | "lib/" 9 | ], 10 | "scripts": { 11 | "lint": "eslint . --fix --ext .ts", 12 | "clean": "npx del-cli lib", 13 | "test": "jest", 14 | "test16": "jest ./src/issue#16.spec.ts", 15 | "prepare": "npm run lint && tsc && npx del-cli lib/**/*.spec.* " 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/Quobject/aws-cli-js.git" 20 | }, 21 | "keywords": [ 22 | "aws", 23 | "aws-cli" 24 | ], 25 | "author": "Matthias Ludwig ", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/Quobject/aws-cli-js/issues" 29 | }, 30 | "homepage": "https://github.com/Quobject/aws-cli-js", 31 | "devDependencies": { 32 | "@types/jest": "^26.0.20", 33 | "@types/lodash": "^4.14.168", 34 | "@types/node": "^14.14.25", 35 | "@typescript-eslint/eslint-plugin": "^4.14.2", 36 | "@typescript-eslint/parser": "^4.13.0", 37 | "del-cli": "^3.0.1", 38 | "eslint": "^7.17.0", 39 | "eslint-config-airbnb-base": "^14.2.1", 40 | "eslint-plugin-import": "^2.22.1", 41 | "jest": "^26.6.3", 42 | "ts-jest": "^26.4.4", 43 | "ts-node": "^9.1.1", 44 | "typescript": "^4.1.3" 45 | }, 46 | "dependencies": { 47 | "nodeify-ts": "1.0.6" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { Aws, Options } from './index'; 2 | import * as util from 'util'; 3 | 4 | /* eslint-disable @typescript-eslint/no-var-requires */ 5 | const config = require('../my_config.json'); 6 | 7 | //console.log('config', config); 8 | 9 | describe('iam list-users', () => { 10 | it('should work', () => { 11 | const result = true; 12 | 13 | expect(result).toBeTruthy(); 14 | const options = new Options( 15 | /* accessKey */ config.accessKeyId, 16 | /* secretKey */ config.secretAccessKey, 17 | /* sessionToken */ config.sessionToken, 18 | /* currentWorkingDirectory */ undefined, 19 | /* cliPath */ 'aws', 20 | ); 21 | 22 | 23 | const aws = new Aws(options); 24 | 25 | //const command = 'iam list-users --region us-west-1'; 26 | const command = `iam \ 27 | list-users \ 28 | --region us-west-1`; 29 | 30 | 31 | return aws.command(command).then((data: any) => { 32 | //console.log('data = ', util.inspect(data, { depth: 10 })); 33 | expect(data).toBeTruthy(); 34 | expect(data.object.Users).toBeTruthy(); 35 | }); 36 | 37 | 38 | }); 39 | }); 40 | 41 | 42 | describe('iam list-users', () => { 43 | it('should fail with invalid sessionToken', () => { 44 | const result = true; 45 | 46 | expect(result).toBeTruthy(); 47 | const options = new Options( 48 | /* accessKey */ config.accessKeyId, 49 | /* secretKey */ config.secretAccessKey, 50 | /* sessionToken */ 'invalid', 51 | /* currentWorkingDirectory */ undefined, 52 | ); 53 | 54 | 55 | const aws = new Aws(options); 56 | 57 | let flag = false; 58 | return aws.command('iam list-users').then((data: any) => { 59 | flag = true; 60 | }).catch((r) => { 61 | expect(flag).toBeFalsy(); 62 | //console.log('r = ', r); 63 | }); 64 | 65 | 66 | }); 67 | }); 68 | 69 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as child_process from 'child_process'; 2 | 3 | import nodeify from 'nodeify-ts'; 4 | 5 | const execFile = child_process.execFile; 6 | const exec = child_process.exec; 7 | 8 | const extractResult = (result: Result): Result => { 9 | try { 10 | result.object = JSON.parse(result.raw); 11 | } catch (e) { 12 | result.object = e; 13 | } 14 | return result; 15 | }; 16 | 17 | export class Aws { 18 | 19 | constructor(private options: IOptions = { 20 | accessKey: undefined, 21 | currentWorkingDirectory: undefined, 22 | secretKey: undefined, 23 | cliPath: 'aws' 24 | }) { } 25 | 26 | public command(command: string, callback?: (err: any, data: any) => void): Promise { 27 | 28 | const execCommand = `${this.options.cliPath} ${command}`; 29 | 30 | const env_vars = ('HOME PATH AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY ' + 31 | 'AWS_SESSION_TOKEN AWS_DEFAULT_REGION ' + 32 | 'AWS_PROFILE AWS_DEFAULT_PROFILE AWS_CONFIG_FILE').split(' '); 33 | 34 | 35 | const env = env_vars.reduce((result: any, value: string) => { 36 | if (process.env[value]) { 37 | result[value] = process.env[value]; 38 | } 39 | return result; 40 | }, {}); 41 | 42 | env['DEBUG'] = ''; 43 | 44 | if (this.options.accessKey) { 45 | env['AWS_ACCESS_KEY_ID'] = this.options.accessKey; 46 | } 47 | 48 | if (this.options.secretKey) { 49 | env['AWS_SECRET_ACCESS_KEY'] = this.options.secretKey; 50 | } 51 | 52 | if (this.options.sessionToken) { 53 | env['AWS_SESSION_TOKEN'] = this.options.sessionToken; 54 | } 55 | 56 | const execOptions = { 57 | cwd: this.options.currentWorkingDirectory, 58 | env: env, 59 | maxBuffer: 200 * 1024 * 1024, 60 | }; 61 | 62 | 63 | const promise = Promise.resolve().then(() => { 64 | 65 | 66 | return new Promise<{ stderr: string, stdout: string }>((resolve, reject) => { 67 | 68 | if (command.indexOf('"') < 0) { 69 | let cmd = [...command.split(' ')]; 70 | cmd = cmd.filter(v => v.length > 0); 71 | //console.log('execFile'); 72 | execFile(this.options.cliPath, cmd, execOptions, (error: Error | null, stdout: string, stderr: string) => { 73 | if (error) { 74 | const message = `error: '${error}' stdout = '${stdout}' stderr = '${stderr}'`; 75 | //console.error(message); 76 | reject(message); 77 | } 78 | //console.log(`stdout: ${stdout}`); 79 | resolve({ stderr: stderr, stdout: stdout }); 80 | }); 81 | } else { 82 | //console.log('exec'); 83 | exec(execCommand, execOptions, (error: Error | null, stdout: string, stderr: string) => { 84 | if (error) { 85 | const message = `error: '${error}' stdout = '${stdout}' stderr = '${stderr}'`; 86 | //console.error(message); 87 | reject(message); 88 | } 89 | //console.log(`stdout: ${stdout}`); 90 | resolve({ stderr: stderr, stdout: stdout }); 91 | }); 92 | } 93 | }); 94 | 95 | }).then((data: { stderr: string, stdout: string }) => { 96 | 97 | const result: Result = { 98 | command: execCommand, 99 | error: data.stderr, 100 | object: null, 101 | raw: data.stdout, 102 | }; 103 | return extractResult(result); 104 | }); 105 | 106 | return nodeify(promise, callback); 107 | } 108 | } 109 | 110 | 111 | export interface IOptions { 112 | accessKey?: string; 113 | secretKey?: string; 114 | sessionToken?: string; 115 | currentWorkingDirectory?: string; 116 | cliPath: string; 117 | } 118 | 119 | interface Result { 120 | command: string; 121 | error: string; 122 | raw: string; 123 | object: any; 124 | } 125 | 126 | export class Options implements IOptions { 127 | public constructor( 128 | public accessKey?: string, 129 | public secretKey?: string, 130 | public sessionToken?: string, 131 | public currentWorkingDirectory?: string, 132 | public cliPath = 'aws') { } 133 | } 134 | 135 | -------------------------------------------------------------------------------- /src/issue#16.spec.ts: -------------------------------------------------------------------------------- 1 | import * as util from 'util'; 2 | 3 | import { Aws, Options } from './index'; 4 | 5 | /* eslint-disable @typescript-eslint/no-var-requires */ 6 | const config = require('../my_config.json'); 7 | 8 | //console.log('config', config); 9 | //https://github.com/Quobject/aws-cli-js/issues/16 10 | 11 | 12 | describe('Using space inside parameter https://github.com/Quobject/aws-cli-js/issues/16', () => { 13 | const options = new Options( 14 | /* accessKey */ config.accessKeyId, 15 | /* secretKey */ config.secretAccessKey, 16 | /* sessionToken */ config.sessionToken, 17 | /* currentWorkingDirectory */ undefined, 18 | /* cliPath */ 'aws', 19 | ); 20 | 21 | const aws = new Aws(options); 22 | 23 | it('with double quotes', () => { 24 | const command = 's3api put-object --bucket test.quobject.io --key pic.png --body ./testdata/smiley.png --content-type image/png --cache-control "public, max-age=31536000"'; 25 | return aws.command(command).then((data: any) => { 26 | console.log('data = ', util.inspect(data, { depth: 10 })); 27 | expect(data).toBeTruthy(); 28 | }); 29 | }); 30 | 31 | it('without double quotes', () => { 32 | const command = 's3api put-object --bucket test.quobject.io --key pic.png --body ./testdata/smiley.png --content-type image/png --cache-control max-age=31536000'; 33 | return aws.command(command).then((data: any) => { 34 | console.log('data = ', util.inspect(data, { depth: 10 })); 35 | expect(data).toBeTruthy(); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /testdata/smiley.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quobject/aws-cli-js/657e0cd5177d5ad4669bf72b2f2a17b03938f81e/testdata/smiley.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "ES6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 8 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 9 | // "lib": [], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 13 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | "outDir": "lib", /* Redirect output structure to the directory. */ 18 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true, /* Enable all strict type-checking options. */ 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 43 | 44 | /* Module Resolution Options */ 45 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 46 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 47 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 49 | "typeRoots": [ "node_modules/@types" ], /* List of folders to include type definitions from. */ 50 | // "types": [], /* Type declaration files to be included in compilation. */ 51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 52 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 55 | 56 | /* Source Map Options */ 57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 61 | 62 | /* Experimental Options */ 63 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 64 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 65 | 66 | /* Advanced Options */ 67 | "skipLibCheck": true, /* Skip type checking of declaration files. */ 68 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 69 | }, 70 | "exclude": [ 71 | "lib/**/*", 72 | "node_modules", 73 | ] 74 | } 75 | --------------------------------------------------------------------------------