├── .gitignore ├── 01-webotron ├── Pipfile ├── Pipfile.lock ├── index.html ├── ipythonsession.py ├── kitten_web │ ├── css │ │ └── main.css │ ├── images │ │ ├── Balinese-kitten1.jpg │ │ ├── Maine_coon_kitten_roarie.jpg │ │ └── SFSPCA_Kitten.jpg │ └── index.html ├── lint.sh ├── setup.py └── webotron │ ├── __init__.py │ ├── bucket.py │ ├── cdn.py │ ├── certificate.py │ ├── domain.py │ ├── util.py │ └── webotron.py ├── 02-notifon ├── Pipfile ├── Pipfile.lock ├── autoscale_example.py ├── config.tmpl.json ├── ec2_example.py ├── notifier │ ├── .gitignore │ ├── handler.py │ ├── package-lock.json │ ├── package.json │ ├── requirements.txt │ ├── serverless.yml │ └── setup.cfg ├── post_to_slack.py └── scale-up.py ├── 03-videolyzer ├── Pipfile ├── Pipfile.lock ├── config.tmpl.json ├── handle-sns-event-example.py ├── label-detection.py ├── s3-event-example.py ├── upload-file.py └── videolyzer │ ├── .gitignore │ ├── handler.py │ ├── package-lock.json │ └── serverless.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | config.dev.json 4 | 5 | *.pem 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # pyenv 82 | .python-version 83 | 84 | # celery beat schedule file 85 | celerybeat-schedule 86 | 87 | # SageMath parsed files 88 | *.sage.py 89 | 90 | # Environments 91 | .env 92 | .venv 93 | env/ 94 | venv/ 95 | ENV/ 96 | env.bak/ 97 | venv.bak/ 98 | 99 | # Spyder project settings 100 | .spyderproject 101 | .spyproject 102 | 103 | # Rope project settings 104 | .ropeproject 105 | 106 | # mkdocs documentation 107 | /site 108 | 109 | # mypy 110 | .mypy_cache/ 111 | -------------------------------------------------------------------------------- /01-webotron/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | "boto3" = "*" 8 | click = "*" 9 | 10 | [dev-packages] 11 | ipython = "*" 12 | pycodestyle = "*" 13 | pydocstyle = "*" 14 | pylint = "*" 15 | pyflakes = "*" 16 | setuptools = "*" 17 | 18 | [requires] 19 | python_version = "3.6" 20 | -------------------------------------------------------------------------------- /01-webotron/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "7a88f65bfc22364b577cc82293c486ba951a9325ac84d06d02d755489083b680" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.6" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "boto3": { 20 | "hashes": [ 21 | "sha256:14e53a29a604f5d1ae955a3748a23e37be7c33298cfe545ffca2313ab9393634", 22 | "sha256:7d3ed0ba56f3c4e8b44522e2fc661c7dc1975b0ed3989d1c0aee75184b72a49b" 23 | ], 24 | "index": "pypi", 25 | "version": "==1.7.40" 26 | }, 27 | "botocore": { 28 | "hashes": [ 29 | "sha256:42fb33f3fdf976a18f1392f6dcf9222a1e91269a6019f4997c675a5b1392fd0e", 30 | "sha256:8873388deff33683bc437dd615ada5862c721754accf379ebe4ce0e21cbe2544" 31 | ], 32 | "version": "==1.10.40" 33 | }, 34 | "click": { 35 | "hashes": [ 36 | "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", 37 | "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" 38 | ], 39 | "index": "pypi", 40 | "version": "==6.7" 41 | }, 42 | "docutils": { 43 | "hashes": [ 44 | "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", 45 | "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", 46 | "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6" 47 | ], 48 | "version": "==0.14" 49 | }, 50 | "jmespath": { 51 | "hashes": [ 52 | "sha256:6a81d4c9aa62caf061cb517b4d9ad1dd300374cd4706997aff9cd6aedd61fc64", 53 | "sha256:f11b4461f425740a1d908e9a3f7365c3d2e569f6ca68a2ff8bc5bcd9676edd63" 54 | ], 55 | "version": "==0.9.3" 56 | }, 57 | "python-dateutil": { 58 | "hashes": [ 59 | "sha256:1adb80e7a782c12e52ef9a8182bebeb73f1d7e24e374397af06fb4956c8dc5c0", 60 | "sha256:e27001de32f627c22380a688bcc43ce83504a7bc5da472209b4c70f02829f0b8" 61 | ], 62 | "markers": "python_version >= '2.7'", 63 | "version": "==2.7.3" 64 | }, 65 | "s3transfer": { 66 | "hashes": [ 67 | "sha256:90dc18e028989c609146e241ea153250be451e05ecc0c2832565231dacdf59c1", 68 | "sha256:c7a9ec356982d5e9ab2d4b46391a7d6a950e2b04c472419f5fdec70cc0ada72f" 69 | ], 70 | "version": "==0.1.13" 71 | }, 72 | "six": { 73 | "hashes": [ 74 | "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", 75 | "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" 76 | ], 77 | "version": "==1.11.0" 78 | } 79 | }, 80 | "develop": { 81 | "appnope": { 82 | "hashes": [ 83 | "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", 84 | "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71" 85 | ], 86 | "markers": "sys_platform == 'darwin'", 87 | "version": "==0.1.0" 88 | }, 89 | "astroid": { 90 | "hashes": [ 91 | "sha256:0ef2bf9f07c3150929b25e8e61b5198c27b0dca195e156f0e4d5bdd89185ca1a", 92 | "sha256:fc9b582dba0366e63540982c3944a9230cbc6f303641c51483fa547dcc22393a" 93 | ], 94 | "version": "==1.6.5" 95 | }, 96 | "backcall": { 97 | "hashes": [ 98 | "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", 99 | "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2" 100 | ], 101 | "version": "==0.1.0" 102 | }, 103 | "decorator": { 104 | "hashes": [ 105 | "sha256:2c51dff8ef3c447388fe5e4453d24a2bf128d3a4c32af3fabef1f01c6851ab82", 106 | "sha256:c39efa13fbdeb4506c476c9b3babf6a718da943dab7811c206005a4a956c080c" 107 | ], 108 | "version": "==4.3.0" 109 | }, 110 | "ipython": { 111 | "hashes": [ 112 | "sha256:a0c96853549b246991046f32d19db7140f5b1a644cc31f0dc1edc86713b7676f", 113 | "sha256:eca537aa61592aca2fef4adea12af8e42f5c335004dfa80c78caf80e8b525e5c" 114 | ], 115 | "index": "pypi", 116 | "version": "==6.4.0" 117 | }, 118 | "ipython-genutils": { 119 | "hashes": [ 120 | "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", 121 | "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" 122 | ], 123 | "version": "==0.2.0" 124 | }, 125 | "isort": { 126 | "hashes": [ 127 | "sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af", 128 | "sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8", 129 | "sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497" 130 | ], 131 | "version": "==4.3.4" 132 | }, 133 | "jedi": { 134 | "hashes": [ 135 | "sha256:1972f694c6bc66a2fac8718299e2ab73011d653a6d8059790c3476d2353b99ad", 136 | "sha256:5861f6dc0c16e024cbb0044999f9cf8013b292c05f287df06d3d991a87a4eb89" 137 | ], 138 | "version": "==0.12.0" 139 | }, 140 | "lazy-object-proxy": { 141 | "hashes": [ 142 | "sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33", 143 | "sha256:1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39", 144 | "sha256:209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019", 145 | "sha256:27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088", 146 | "sha256:27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b", 147 | "sha256:2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e", 148 | "sha256:2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6", 149 | "sha256:320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b", 150 | "sha256:50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5", 151 | "sha256:5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff", 152 | "sha256:61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd", 153 | "sha256:6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7", 154 | "sha256:7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff", 155 | "sha256:7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d", 156 | "sha256:7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2", 157 | "sha256:7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35", 158 | "sha256:81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4", 159 | "sha256:933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514", 160 | "sha256:94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252", 161 | "sha256:ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109", 162 | "sha256:bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f", 163 | "sha256:cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c", 164 | "sha256:d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92", 165 | "sha256:ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577", 166 | "sha256:e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d", 167 | "sha256:e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d", 168 | "sha256:e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f", 169 | "sha256:eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a", 170 | "sha256:f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b" 171 | ], 172 | "version": "==1.3.1" 173 | }, 174 | "mccabe": { 175 | "hashes": [ 176 | "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", 177 | "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" 178 | ], 179 | "version": "==0.6.1" 180 | }, 181 | "parso": { 182 | "hashes": [ 183 | "sha256:cdef26e8adc10d589f3ec4eb444bd0a29f3f1eb6d72a4292ab8afcb9d68976a6", 184 | "sha256:f0604a40b96e062b0fd99cf134cc2d5cdf66939d0902f8267d938b0d5b26707f" 185 | ], 186 | "version": "==0.2.1" 187 | }, 188 | "pexpect": { 189 | "hashes": [ 190 | "sha256:2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba", 191 | "sha256:3fbd41d4caf27fa4a377bfd16fef87271099463e6fa73e92a52f92dfee5d425b" 192 | ], 193 | "markers": "sys_platform != 'win32'", 194 | "version": "==4.6.0" 195 | }, 196 | "pickleshare": { 197 | "hashes": [ 198 | "sha256:84a9257227dfdd6fe1b4be1319096c20eb85ff1e82c7932f36efccfe1b09737b", 199 | "sha256:c9a2541f25aeabc070f12f452e1f2a8eae2abd51e1cd19e8430402bdf4c1d8b5" 200 | ], 201 | "version": "==0.7.4" 202 | }, 203 | "prompt-toolkit": { 204 | "hashes": [ 205 | "sha256:1df952620eccb399c53ebb359cc7d9a8d3a9538cb34c5a1344bdbeb29fbcc381", 206 | "sha256:3f473ae040ddaa52b52f97f6b4a493cfa9f5920c255a12dc56a7d34397a398a4", 207 | "sha256:858588f1983ca497f1cf4ffde01d978a3ea02b01c8a26a8bbc5cd2e66d816917" 208 | ], 209 | "version": "==1.0.15" 210 | }, 211 | "ptyprocess": { 212 | "hashes": [ 213 | "sha256:e64193f0047ad603b71f202332ab5527c5e52aa7c8b609704fc28c0dc20c4365", 214 | "sha256:e8c43b5eee76b2083a9badde89fd1bbce6c8942d1045146e100b7b5e014f4f1a" 215 | ], 216 | "version": "==0.5.2" 217 | }, 218 | "pycodestyle": { 219 | "hashes": [ 220 | "sha256:74abc4e221d393ea5ce1f129ea6903209940c1ecd29e002e8c6933c2b21026e0", 221 | "sha256:cbc619d09254895b0d12c2c691e237b2e91e9b2ecf5e84c26b35400f93dcfb83", 222 | "sha256:cbfca99bd594a10f674d0cd97a3d802a1fdef635d4361e1a2658de47ed261e3a" 223 | ], 224 | "index": "pypi", 225 | "version": "==2.4.0" 226 | }, 227 | "pydocstyle": { 228 | "hashes": [ 229 | "sha256:08a870edc94508264ed90510db466c6357c7192e0e866561d740624a8fc7d90c", 230 | "sha256:4d5bcde961107873bae621f3d580c3e35a426d3687ffc6f8fb356f6628da5a97", 231 | "sha256:af9fcccb303899b83bec82dc9a1d56c60fc369973223a5e80c3dfa9bdf984405" 232 | ], 233 | "index": "pypi", 234 | "version": "==2.1.1" 235 | }, 236 | "pyflakes": { 237 | "hashes": [ 238 | "sha256:9a7662ec724d0120012f6e29d6248ae3727d821bba522a0e6b356eff19126a49", 239 | "sha256:f661252913bc1dbe7fcfcbf0af0db3f42ab65aabd1a6ca68fe5d466bace94dae" 240 | ], 241 | "index": "pypi", 242 | "version": "==2.0.0" 243 | }, 244 | "pygments": { 245 | "hashes": [ 246 | "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d", 247 | "sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc" 248 | ], 249 | "version": "==2.2.0" 250 | }, 251 | "pylint": { 252 | "hashes": [ 253 | "sha256:a48070545c12430cfc4e865bf62f5ad367784765681b3db442d8230f0960aa3c", 254 | "sha256:fff220bcb996b4f7e2b0f6812fd81507b72ca4d8c4d05daf2655c333800cb9b3" 255 | ], 256 | "index": "pypi", 257 | "version": "==1.9.2" 258 | }, 259 | "simplegeneric": { 260 | "hashes": [ 261 | "sha256:dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173" 262 | ], 263 | "version": "==0.8.1" 264 | }, 265 | "six": { 266 | "hashes": [ 267 | "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", 268 | "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" 269 | ], 270 | "version": "==1.11.0" 271 | }, 272 | "snowballstemmer": { 273 | "hashes": [ 274 | "sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128", 275 | "sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89" 276 | ], 277 | "version": "==1.2.1" 278 | }, 279 | "traitlets": { 280 | "hashes": [ 281 | "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", 282 | "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9" 283 | ], 284 | "version": "==4.3.2" 285 | }, 286 | "wcwidth": { 287 | "hashes": [ 288 | "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", 289 | "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" 290 | ], 291 | "version": "==0.1.7" 292 | }, 293 | "wrapt": { 294 | "hashes": [ 295 | "sha256:d4d560d479f2c21e1b5443bbd15fe7ec4b37fe7e53d335d3b9b0a7b1226fe3c6" 296 | ], 297 | "version": "==1.10.11" 298 | } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /01-webotron/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | My First Website 6 | 7 | 8 |

This is my website. There are many like it, but this one is mine.

9 | 10 | 11 | -------------------------------------------------------------------------------- /01-webotron/ipythonsession.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import boto3 3 | session = boto3.Session(profile_name='pythonAutomation') 4 | s3 = session.resource('s3') 5 | -------------------------------------------------------------------------------- /01-webotron/kitten_web/css/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACloudGuru/automating-aws-with-python/646afe5743233cbdc3502d0b194d65529412f259/01-webotron/kitten_web/css/main.css -------------------------------------------------------------------------------- /01-webotron/kitten_web/images/Balinese-kitten1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACloudGuru/automating-aws-with-python/646afe5743233cbdc3502d0b194d65529412f259/01-webotron/kitten_web/images/Balinese-kitten1.jpg -------------------------------------------------------------------------------- /01-webotron/kitten_web/images/Maine_coon_kitten_roarie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACloudGuru/automating-aws-with-python/646afe5743233cbdc3502d0b194d65529412f259/01-webotron/kitten_web/images/Maine_coon_kitten_roarie.jpg -------------------------------------------------------------------------------- /01-webotron/kitten_web/images/SFSPCA_Kitten.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACloudGuru/automating-aws-with-python/646afe5743233cbdc3502d0b194d65529412f259/01-webotron/kitten_web/images/SFSPCA_Kitten.jpg -------------------------------------------------------------------------------- /01-webotron/kitten_web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Kittens, kittens, kittens! 6 | 7 | 8 |

This is a kitten-themed website. It contains all kitten-related facts you'll ever need.

9 | 14 |

We also have some pictures of kittens.

15 | Balinese kitten 16 | 20 | Maine Coon kitten 21 | 25 | Kitten 26 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /01-webotron/lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | pycodestyle webotron/ 3 | pydocstyle webotron/ 4 | pyflakes webotron/ 5 | -------------------------------------------------------------------------------- /01-webotron/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='webotron-80', 5 | version='0.1', 6 | author='Robin Norwood', 7 | author_email='robin@acloud.guru', 8 | description='Webotron 80 is a tool to deploy static websites to AWS.', 9 | license='GPLv3+', 10 | packages=['webotron'], 11 | url='https://github.com/ACloudGuru/automating-aws-with-python/tree/master/01-webotron', 12 | install_requires=[ 13 | 'click', 14 | 'boto3' 15 | ], 16 | entry_points=''' 17 | [console_scripts] 18 | webotron=webotron.webotron:cli 19 | ''' 20 | ) 21 | -------------------------------------------------------------------------------- /01-webotron/webotron/__init__.py: -------------------------------------------------------------------------------- 1 | """Webotron script and modules. Deploy websites to S3.""" 2 | -------------------------------------------------------------------------------- /01-webotron/webotron/bucket.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Classes for S3 Buckets.""" 4 | 5 | from pathlib import Path 6 | import mimetypes 7 | from functools import reduce 8 | 9 | import boto3 10 | from botocore.exceptions import ClientError 11 | 12 | from hashlib import md5 13 | from webotron import util 14 | 15 | 16 | class BucketManager: 17 | """Manage an S3 Bucket.""" 18 | 19 | CHUNK_SIZE = 8388608 20 | 21 | def __init__(self, session): 22 | """Create a BucketManager object.""" 23 | self.session = session 24 | self.s3 = self.session.resource('s3') 25 | self.transfer_config = boto3.s3.transfer.TransferConfig( 26 | multipart_chunksize=self.CHUNK_SIZE, 27 | multipart_threshold=self.CHUNK_SIZE 28 | ) 29 | self.manifest = {} 30 | 31 | def get_bucket(self, bucket_name): 32 | """Get a bucket by name.""" 33 | return self.s3.Bucket(bucket_name) 34 | 35 | def get_region_name(self, bucket): 36 | """Get the bucket's region name.""" 37 | client = self.s3.meta.client 38 | bucket_location = client.get_bucket_location(Bucket=bucket.name) 39 | 40 | return bucket_location["LocationConstraint"] or 'us-east-1' 41 | 42 | def get_bucket_url(self, bucket): 43 | """Get the website URL for this bucket.""" 44 | return "http://{}.{}".format( 45 | bucket.name, 46 | util.get_endpoint(self.get_region_name(bucket)).host 47 | ) 48 | 49 | def all_buckets(self): 50 | """Get an iterator for all buckets.""" 51 | return self.s3.buckets.all() 52 | 53 | def all_objects(self, bucket_name): 54 | """Get an iterator for all objects in bucket.""" 55 | return self.s3.Bucket(bucket_name).objects.all() 56 | 57 | def init_bucket(self, bucket_name): 58 | """Create new bucket, or return existing one by name.""" 59 | s3_bucket = None 60 | try: 61 | s3_bucket = self.s3.create_bucket( 62 | Bucket=bucket_name, 63 | CreateBucketConfiguration={ 64 | 'LocationConstraint': self.session.region_name 65 | } 66 | ) 67 | except ClientError as error: 68 | if error.response['Error']['Code'] == 'BucketAlreadyOwnedByYou': 69 | s3_bucket = self.s3.Bucket(bucket_name) 70 | else: 71 | raise error 72 | 73 | return s3_bucket 74 | 75 | def set_policy(self, bucket): 76 | """Set bucket policy to be readable by everyone.""" 77 | policy = """ 78 | { 79 | "Version":"2012-10-17", 80 | "Statement":[{ 81 | "Sid":"PublicReadGetObject", 82 | "Effect":"Allow", 83 | "Principal": "*", 84 | "Action":["s3:GetObject"], 85 | "Resource":["arn:aws:s3:::%s/*" 86 | ] 87 | } 88 | ] 89 | } 90 | """ % bucket.name 91 | policy = policy.strip() 92 | 93 | pol = bucket.Policy() 94 | pol.put(Policy=policy) 95 | 96 | def configure_website(self, bucket): 97 | """Configure s3 website hosting for bucket.""" 98 | bucket.Website().put(WebsiteConfiguration={ 99 | 'ErrorDocument': { 100 | 'Key': 'error.html' 101 | }, 102 | 'IndexDocument': { 103 | 'Suffix': 'index.html' 104 | } 105 | }) 106 | 107 | def load_manifest(self, bucket): 108 | """Load manifest for caching purposes.""" 109 | paginator = self.s3.meta.client.get_paginator('list_objects_v2') 110 | for page in paginator.paginate(Bucket=bucket.name): 111 | for obj in page.get('Contents', []): 112 | self.manifest[obj['Key']] = obj['ETag'] 113 | 114 | @staticmethod 115 | def hash_data(data): 116 | """Generate md5 hash for data.""" 117 | hash = md5() 118 | hash.update(data) 119 | 120 | return hash 121 | 122 | def gen_etag(self, path): 123 | """Generate etag for file.""" 124 | hashes = [] 125 | 126 | with open(path, 'rb') as f: 127 | while True: 128 | data = f.read(self.CHUNK_SIZE) 129 | 130 | if not data: 131 | break 132 | 133 | hashes.append(self.hash_data(data)) 134 | 135 | if not hashes: 136 | return 137 | elif len(hashes) == 1: 138 | return '"{}"'.format(hashes[0].hexdigest()) 139 | else: 140 | digests = (h.digest() for h in hashes) 141 | hash = self.hash_data(reduce(lambda x, y: x + y, digests)) 142 | return '"{}-{}"'.format(hash.hexdigest(), len(hashes)) 143 | 144 | def upload_file(self, bucket, path, key): 145 | """Upload path to s3_bucket at key.""" 146 | content_type = mimetypes.guess_type(key)[0] or 'text/plain' 147 | 148 | etag = self.gen_etag(path) 149 | if self.manifest.get(key, '') == etag: 150 | return 151 | 152 | return bucket.upload_file( 153 | path, 154 | key, 155 | ExtraArgs={ 156 | 'ContentType': content_type 157 | }, 158 | Config=self.transfer_config 159 | ) 160 | 161 | def sync(self, pathname, bucket_name): 162 | """Sync contents of path to bucket.""" 163 | bucket = self.s3.Bucket(bucket_name) 164 | self.load_manifest(bucket) 165 | 166 | root = Path(pathname).expanduser().resolve() 167 | 168 | def handle_directory(target): 169 | for p in target.iterdir(): 170 | if p.is_dir(): 171 | handle_directory(p) 172 | if p.is_file(): 173 | self.upload_file(bucket, str(p), str(p.relative_to(root))) 174 | 175 | handle_directory(root) 176 | -------------------------------------------------------------------------------- /01-webotron/webotron/cdn.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Classes for Cloud Front Distributions.""" 4 | 5 | import uuid 6 | 7 | 8 | class DistributionManager: 9 | """Manage CloudFront distributions.""" 10 | 11 | def __init__(self, session): 12 | """Create a DistributionManager.""" 13 | self.session = session 14 | self.client = self.session.client('cloudfront') 15 | 16 | def find_matching_dist(self, domain_name): 17 | """Find a dist matching domain_name.""" 18 | paginator = self.client.get_paginator('list_distributions') 19 | for page in paginator.paginate(): 20 | print(page) 21 | for dist in page['DistributionList'].get('Items', []): 22 | for alias in dist['Aliases']['Items']: 23 | if alias == domain_name: 24 | return dist 25 | 26 | return None 27 | 28 | def create_dist(self, domain_name, cert): 29 | """Create a dist for domain_name using cert.""" 30 | origin_id = 'S3-' + domain_name 31 | 32 | result = self.client.create_distribution( 33 | DistributionConfig={ 34 | 'CallerReference': str(uuid.uuid4()), 35 | 'Aliases': { 36 | 'Quantity': 1, 37 | 'Items': [domain_name] 38 | }, 39 | 'DefaultRootObject': 'index.html', 40 | 'Comment': 'Created by webotron', 41 | 'Enabled': True, 42 | 'Origins': { 43 | 'Quantity': 1, 44 | 'Items': [{ 45 | 'Id': origin_id, 46 | 'DomainName': 47 | '{}.s3.amazonaws.com'.format(domain_name), 48 | 'S3OriginConfig': { 49 | 'OriginAccessIdentity': '' 50 | } 51 | }] 52 | }, 53 | 'DefaultCacheBehavior': { 54 | 'TargetOriginId': origin_id, 55 | 'ViewerProtocolPolicy': 'redirect-to-https', 56 | 'TrustedSigners': { 57 | 'Quantity': 0, 58 | 'Enabled': False 59 | }, 60 | 'ForwardedValues': { 61 | 'Cookies': {'Forward': 'all'}, 62 | 'Headers': {'Quantity': 0}, 63 | 'QueryString': False, 64 | 'QueryStringCacheKeys': {'Quantity': 0} 65 | }, 66 | 'DefaultTTL': 86400, 67 | 'MinTTL': 3600 68 | }, 69 | 'ViewerCertificate': { 70 | 'ACMCertificateArn': cert['CertificateArn'], 71 | 'SSLSupportMethod': 'sni-only', 72 | 'MinimumProtocolVersion': 'TLSv1.1_2016' 73 | } 74 | } 75 | ) 76 | 77 | return result['Distribution'] 78 | 79 | def await_deploy(self, dist): 80 | """Wait for dist to be deployed.""" 81 | waiter = self.client.get_waiter('distribution_deployed') 82 | waiter.wait(Id=dist['Id'], WaiterConfig={ 83 | 'Delay': 30, 84 | 'MaxAttempts': 50 85 | }) 86 | -------------------------------------------------------------------------------- /01-webotron/webotron/certificate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Classes for ACM Certificates.""" 4 | 5 | 6 | class CertificateManager: 7 | """Manage an ACM Certificate.""" 8 | 9 | def __init__(self, session): 10 | """Create a CertificateManager.""" 11 | self.session = session 12 | self.client = self.session.client('acm', region_name='us-east-1') 13 | 14 | def cert_matches(self, cert_arn, domain_name): 15 | """Return True if cert matches domain_name.""" 16 | cert_details = self.client.describe_certificate( 17 | CertificateArn=cert_arn 18 | ) 19 | alt_names = cert_details['Certificate']['SubjectAlternativeNames'] 20 | for name in alt_names: 21 | if name == domain_name: 22 | return True 23 | if name[0] == '*' and domain_name.endswith(name[1:]): 24 | return True 25 | return False 26 | 27 | def find_matching_cert(self, domain_name): 28 | """Find a certificate matching domain_name.""" 29 | paginator = self.client.get_paginator('list_certificates') 30 | for page in paginator.paginate(CertificateStatuses=['ISSUED']): 31 | for cert in page['CertificateSummaryList']: 32 | if self.cert_matches(cert['CertificateArn'], domain_name): 33 | return cert 34 | 35 | return None 36 | -------------------------------------------------------------------------------- /01-webotron/webotron/domain.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Classes for Route 53 domains.""" 4 | 5 | import uuid 6 | 7 | 8 | class DomainManager: 9 | """Manage a Route 53 domain.""" 10 | 11 | def __init__(self, session): 12 | """Create DomainManager object.""" 13 | self.session = session 14 | self.client = self.session.client('route53') 15 | 16 | def find_hosted_zone(self, domain_name): 17 | """Find zone matching domain_name.""" 18 | paginator = self.client.get_paginator('list_hosted_zones') 19 | for page in paginator.paginate(): 20 | for zone in page['HostedZones']: 21 | if domain_name.endswith(zone['Name'][:-1]): 22 | return zone 23 | 24 | return None 25 | 26 | def create_hosted_zone(self, domain_name): 27 | """Create a hosted zone to match domain_name.""" 28 | zone_name = '.'.join(domain_name.split('.')[-2:]) + '.' 29 | return self.client.create_hosted_zone( 30 | Name=zone_name, 31 | CallerReference=str(uuid.uuid4()) 32 | ) 33 | 34 | def create_s3_domain_record(self, zone, domain_name, endpoint): 35 | """Create a domain record in zone for domain_name.""" 36 | return self.client.change_resource_record_sets( 37 | HostedZoneId=zone['Id'], 38 | ChangeBatch={ 39 | 'Comment': 'Created by webotron', 40 | 'Changes': [{ 41 | 'Action': 'UPSERT', 42 | 'ResourceRecordSet': { 43 | 'Name': domain_name, 44 | 'Type': 'A', 45 | 'AliasTarget': { 46 | 'HostedZoneId': endpoint.zone, 47 | 'DNSName': endpoint.host, 48 | 'EvaluateTargetHealth': False 49 | } 50 | } 51 | } 52 | ] 53 | } 54 | ) 55 | 56 | def create_cf_domain_record(self, zone, domain_name, cf_domain): 57 | """Create a domain record in zone for domain_name.""" 58 | return self.client.change_resource_record_sets( 59 | HostedZoneId=zone['Id'], 60 | ChangeBatch={ 61 | 'Comment': 'Created by webotron', 62 | 'Changes': [{ 63 | 'Action': 'UPSERT', 64 | 'ResourceRecordSet': { 65 | 'Name': domain_name, 66 | 'Type': 'A', 67 | 'AliasTarget': { 68 | 'HostedZoneId': 'Z2FDTNDATAQYW2', 69 | 'DNSName': cf_domain, 70 | 'EvaluateTargetHealth': False 71 | } 72 | } 73 | } 74 | ] 75 | } 76 | ) 77 | -------------------------------------------------------------------------------- /01-webotron/webotron/util.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Utilities for webotron.""" 4 | 5 | from collections import namedtuple 6 | 7 | Endpoint = namedtuple('Endpoint', ['name', 'host', 'zone']) 8 | 9 | region_to_endpoint = { 10 | 'us-east-2': Endpoint('US East (Ohio)', 's3-website.us-east-2.amazonaws.com', 'Z2O1EMRO9K5GLX'), 11 | 'us-east-1': Endpoint('US East (N. Virginia)', 's3-website-us-east-1.amazonaws.com', 'Z3AQBSTGFYJSTF'), 12 | 'us-west-1': Endpoint('US West (N. California)', 's3-website-us-west-1.amazonaws.com', 'Z2F56UZL2M1ACD'), 13 | 'us-west-2': Endpoint('US West (Oregon)', 's3-website-us-west-2.amazonaws.com', 'Z3BJ6K6RIION7M'), 14 | 'ca-central-1': Endpoint('Canada (Central)', 's3-website.ca-central-1.amazonaws.com', 'Z1QDHH18159H29'), 15 | 'ap-south-1': Endpoint('Asia Pacific (Mumbai)', 's3-website.ap-south-1.amazonaws.com', 'Z11RGJOFQNVJUP'), 16 | 'ap-northeast-2': Endpoint('Asia Pacific (Seoul)', 's3-website.ap-northeast-2.amazonaws.com', 'Z3W03O7B5YMIYP'), 17 | 'ap-northeast-3': Endpoint('Asia Pacific (Osaka-Local)', 's3-website.ap-northeast-3.amazonaws.com', 'Z2YQB5RD63NC85'), 18 | 'ap-southeast-1': Endpoint('Asia Pacific (Singapore)', 's3-website-ap-southeast-1.amazonaws.com', 'Z3O0J2DXBE1FTB'), 19 | 'ap-southeast-2': Endpoint('Asia Pacific (Sydney)', 's3-website-ap-southeast-2.amazonaws.com', 'Z1WCIGYICN2BYD'), 20 | 'ap-northeast-1': Endpoint('Asia Pacific (Tokyo)', 's3-website-ap-northeast-1.amazonaws.com', 'Z2M4EHUR26P7ZW'), 21 | 'cn-northwest-1': Endpoint('China (Ningxia)', 's3-website.cn-northwest-1.amazonaws.com.cn', None), 22 | 'eu-central-1': Endpoint('EU (Frankfurt)', 's3-website.eu-central-1.amazonaws.com', 'Z21DNDUVLTQW6Q'), 23 | 'eu-west-1': Endpoint('EU (Ireland)', 's3-website-eu-west-1.amazonaws.com', 'Z1BKCTXD74EZPE'), 24 | 'eu-west-2': Endpoint('EU (London)', 's3-website.eu-west-2.amazonaws.com', 'Z3GKZC51ZF0DB4'), 25 | 'eu-west-3': Endpoint('EU (Paris)', 's3-website.eu-west-3.amazonaws.com', 'Z3R1K369G5AVDG'), 26 | 'sa-east-1': Endpoint('South America (São Paulo)', 's3-website-sa-east-1.amazonaws.com', 'Z7KQH4QJS55SO'), 27 | } 28 | 29 | 30 | def known_region(region): 31 | """Return true if this is a known region.""" 32 | return region in region_to_endpoint 33 | 34 | 35 | def get_endpoint(region): 36 | """Get the s3 website hosting endpoint for this region.""" 37 | return region_to_endpoint[region] 38 | -------------------------------------------------------------------------------- /01-webotron/webotron/webotron.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Webotron: Deploy websites with aws. 5 | 6 | Webotron automates the process of deploying static websites to AWS. 7 | - Configure AWS S3 buckets 8 | - Create them 9 | - Set them up for static website hosting 10 | - Deploy local files to them 11 | - Configure DNS with AWS Route 53 12 | - Configure a Content Delivery Network and SSL with AWS CloudFront 13 | """ 14 | 15 | import boto3 16 | import click 17 | 18 | from webotron.bucket import BucketManager 19 | from webotron.domain import DomainManager 20 | from webotron.certificate import CertificateManager 21 | from webotron.cdn import DistributionManager 22 | 23 | from webotron import util 24 | 25 | session = None 26 | bucket_manager = None 27 | domain_manager = None 28 | cert_manager = None 29 | dist_manager = None 30 | 31 | 32 | @click.group() 33 | @click.option('--profile', default=None, 34 | help="Use a given AWS profile.") 35 | def cli(profile): 36 | """Webotron deploys websites to AWS.""" 37 | global session, bucket_manager, domain_manager, cert_manager, dist_manager 38 | 39 | session_cfg = {} 40 | if profile: 41 | session_cfg['profile_name'] = profile 42 | 43 | session = boto3.Session(**session_cfg) 44 | bucket_manager = BucketManager(session) 45 | domain_manager = DomainManager(session) 46 | cert_manager = CertificateManager(session) 47 | dist_manager = DistributionManager(session) 48 | 49 | 50 | @cli.command('list-buckets') 51 | def list_buckets(): 52 | """List all s3 buckets.""" 53 | for bucket in bucket_manager.all_buckets(): 54 | print(bucket) 55 | 56 | 57 | @cli.command('list-bucket-objects') 58 | @click.argument('bucket') 59 | def list_bucket_objects(bucket): 60 | """List objects in an s3 bucket.""" 61 | for obj in bucket_manager.all_objects(bucket): 62 | print(obj) 63 | 64 | 65 | @cli.command('setup-bucket') 66 | @click.argument('bucket') 67 | def setup_bucket(bucket): 68 | """Create and configure S3 bucket.""" 69 | s3_bucket = bucket_manager.init_bucket(bucket) 70 | bucket_manager.set_policy(s3_bucket) 71 | bucket_manager.configure_website(s3_bucket) 72 | 73 | return 74 | 75 | 76 | @cli.command('sync') 77 | @click.argument('pathname', type=click.Path(exists=True)) 78 | @click.argument('bucket') 79 | def sync(pathname, bucket): 80 | """Sync contents of PATHNAME to BUCKET.""" 81 | bucket_manager.sync(pathname, bucket) 82 | print(bucket_manager.get_bucket_url(bucket_manager.s3.Bucket(bucket))) 83 | 84 | 85 | @cli.command('setup-domain') 86 | @click.argument('domain') 87 | def setup_domain(domain): 88 | """Configure DOMAIN to point to BUCKET.""" 89 | bucket = bucket_manager.get_bucket(domain) 90 | 91 | zone = domain_manager.find_hosted_zone(domain) \ 92 | or domain_manager.create_hosted_zone(domain) 93 | 94 | endpoint = util.get_endpoint(bucket_manager.get_region_name(bucket)) 95 | domain_manager.create_s3_domain_record(zone, domain, endpoint) 96 | print("Domain configured: http://{}".format(domain)) 97 | 98 | 99 | @cli.command('find-cert') 100 | @click.argument('domain') 101 | def find_cert(domain): 102 | """Find a certificate for .""" 103 | print(cert_manager.find_matching_cert(domain)) 104 | 105 | 106 | @cli.command('setup-cdn') 107 | @click.argument('domain') 108 | @click.argument('bucket') 109 | def setup_cdn(domain, bucket): 110 | """Set up CloudFront CDN for DOMAIN pointing to BUCKET.""" 111 | dist = dist_manager.find_matching_dist(domain) 112 | 113 | if not dist: 114 | cert = cert_manager.find_matching_cert(domain) 115 | if not cert: # SSL is not optional at this time 116 | print("Error: No matching cert found.") 117 | return 118 | 119 | dist = dist_manager.create_dist(domain, cert) 120 | print("Waiting for distribution deployment...") 121 | dist_manager.await_deploy(dist) 122 | 123 | zone = domain_manager.find_hosted_zone(domain) \ 124 | or domain_manager.create_hosted_zone(domain) 125 | 126 | domain_manager.create_cf_domain_record(zone, domain, dist['DomainName']) 127 | print("Domain configured: https://{}".format(domain)) 128 | 129 | return 130 | 131 | 132 | if __name__ == '__main__': 133 | cli() 134 | -------------------------------------------------------------------------------- /02-notifon/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | "boto3" = "*" 8 | requests = "*" 9 | 10 | [dev-packages] 11 | ipython = "*" 12 | 13 | [requires] 14 | python_version = "3.6" 15 | -------------------------------------------------------------------------------- /02-notifon/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "1875328eed7b5e2c77fa9516b9f4e2ecbc8fec366e990878add647a241c7822a" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.6" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "boto3": { 20 | "hashes": [ 21 | "sha256:5bd25459c8f506858753cb5e699c4c43af8c9b27e4aae32d9772f677068231b5", 22 | "sha256:f4411a75892d3184324dabeeca23d40e5d0986e0a8c8659ba283ff04d2047068" 23 | ], 24 | "index": "pypi", 25 | "version": "==1.7.44" 26 | }, 27 | "botocore": { 28 | "hashes": [ 29 | "sha256:2202634c6e0dcf4f7886fa87878af8cdba42a340bc6e56ff84515db8ba65cc5b", 30 | "sha256:f59795d7817273ca021b0e2544e78e2b8665a9ab724381afee273e66f86cb7de" 31 | ], 32 | "version": "==1.10.44" 33 | }, 34 | "certifi": { 35 | "hashes": [ 36 | "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7", 37 | "sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0" 38 | ], 39 | "version": "==2018.4.16" 40 | }, 41 | "chardet": { 42 | "hashes": [ 43 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 44 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 45 | ], 46 | "version": "==3.0.4" 47 | }, 48 | "docutils": { 49 | "hashes": [ 50 | "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", 51 | "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", 52 | "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6" 53 | ], 54 | "version": "==0.14" 55 | }, 56 | "idna": { 57 | "hashes": [ 58 | "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", 59 | "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" 60 | ], 61 | "version": "==2.7" 62 | }, 63 | "jmespath": { 64 | "hashes": [ 65 | "sha256:6a81d4c9aa62caf061cb517b4d9ad1dd300374cd4706997aff9cd6aedd61fc64", 66 | "sha256:f11b4461f425740a1d908e9a3f7365c3d2e569f6ca68a2ff8bc5bcd9676edd63" 67 | ], 68 | "version": "==0.9.3" 69 | }, 70 | "python-dateutil": { 71 | "hashes": [ 72 | "sha256:1adb80e7a782c12e52ef9a8182bebeb73f1d7e24e374397af06fb4956c8dc5c0", 73 | "sha256:e27001de32f627c22380a688bcc43ce83504a7bc5da472209b4c70f02829f0b8" 74 | ], 75 | "markers": "python_version >= '2.7'", 76 | "version": "==2.7.3" 77 | }, 78 | "requests": { 79 | "hashes": [ 80 | "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", 81 | "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a" 82 | ], 83 | "index": "pypi", 84 | "version": "==2.19.1" 85 | }, 86 | "s3transfer": { 87 | "hashes": [ 88 | "sha256:90dc18e028989c609146e241ea153250be451e05ecc0c2832565231dacdf59c1", 89 | "sha256:c7a9ec356982d5e9ab2d4b46391a7d6a950e2b04c472419f5fdec70cc0ada72f" 90 | ], 91 | "version": "==0.1.13" 92 | }, 93 | "six": { 94 | "hashes": [ 95 | "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", 96 | "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" 97 | ], 98 | "version": "==1.11.0" 99 | }, 100 | "urllib3": { 101 | "hashes": [ 102 | "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", 103 | "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" 104 | ], 105 | "version": "==1.23" 106 | } 107 | }, 108 | "develop": { 109 | "appnope": { 110 | "hashes": [ 111 | "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", 112 | "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71" 113 | ], 114 | "markers": "sys_platform == 'darwin'", 115 | "version": "==0.1.0" 116 | }, 117 | "backcall": { 118 | "hashes": [ 119 | "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", 120 | "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2" 121 | ], 122 | "version": "==0.1.0" 123 | }, 124 | "decorator": { 125 | "hashes": [ 126 | "sha256:2c51dff8ef3c447388fe5e4453d24a2bf128d3a4c32af3fabef1f01c6851ab82", 127 | "sha256:c39efa13fbdeb4506c476c9b3babf6a718da943dab7811c206005a4a956c080c" 128 | ], 129 | "version": "==4.3.0" 130 | }, 131 | "ipython": { 132 | "hashes": [ 133 | "sha256:a0c96853549b246991046f32d19db7140f5b1a644cc31f0dc1edc86713b7676f", 134 | "sha256:eca537aa61592aca2fef4adea12af8e42f5c335004dfa80c78caf80e8b525e5c" 135 | ], 136 | "index": "pypi", 137 | "version": "==6.4.0" 138 | }, 139 | "ipython-genutils": { 140 | "hashes": [ 141 | "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", 142 | "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" 143 | ], 144 | "version": "==0.2.0" 145 | }, 146 | "jedi": { 147 | "hashes": [ 148 | "sha256:1972f694c6bc66a2fac8718299e2ab73011d653a6d8059790c3476d2353b99ad", 149 | "sha256:5861f6dc0c16e024cbb0044999f9cf8013b292c05f287df06d3d991a87a4eb89" 150 | ], 151 | "version": "==0.12.0" 152 | }, 153 | "parso": { 154 | "hashes": [ 155 | "sha256:cdef26e8adc10d589f3ec4eb444bd0a29f3f1eb6d72a4292ab8afcb9d68976a6", 156 | "sha256:f0604a40b96e062b0fd99cf134cc2d5cdf66939d0902f8267d938b0d5b26707f" 157 | ], 158 | "version": "==0.2.1" 159 | }, 160 | "pexpect": { 161 | "hashes": [ 162 | "sha256:2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba", 163 | "sha256:3fbd41d4caf27fa4a377bfd16fef87271099463e6fa73e92a52f92dfee5d425b" 164 | ], 165 | "markers": "sys_platform != 'win32'", 166 | "version": "==4.6.0" 167 | }, 168 | "pickleshare": { 169 | "hashes": [ 170 | "sha256:84a9257227dfdd6fe1b4be1319096c20eb85ff1e82c7932f36efccfe1b09737b", 171 | "sha256:c9a2541f25aeabc070f12f452e1f2a8eae2abd51e1cd19e8430402bdf4c1d8b5" 172 | ], 173 | "version": "==0.7.4" 174 | }, 175 | "prompt-toolkit": { 176 | "hashes": [ 177 | "sha256:1df952620eccb399c53ebb359cc7d9a8d3a9538cb34c5a1344bdbeb29fbcc381", 178 | "sha256:3f473ae040ddaa52b52f97f6b4a493cfa9f5920c255a12dc56a7d34397a398a4", 179 | "sha256:858588f1983ca497f1cf4ffde01d978a3ea02b01c8a26a8bbc5cd2e66d816917" 180 | ], 181 | "version": "==1.0.15" 182 | }, 183 | "ptyprocess": { 184 | "hashes": [ 185 | "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", 186 | "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f" 187 | ], 188 | "version": "==0.6.0" 189 | }, 190 | "pygments": { 191 | "hashes": [ 192 | "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d", 193 | "sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc" 194 | ], 195 | "version": "==2.2.0" 196 | }, 197 | "simplegeneric": { 198 | "hashes": [ 199 | "sha256:dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173" 200 | ], 201 | "version": "==0.8.1" 202 | }, 203 | "six": { 204 | "hashes": [ 205 | "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", 206 | "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" 207 | ], 208 | "version": "==1.11.0" 209 | }, 210 | "traitlets": { 211 | "hashes": [ 212 | "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", 213 | "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9" 214 | ], 215 | "version": "==4.3.2" 216 | }, 217 | "wcwidth": { 218 | "hashes": [ 219 | "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", 220 | "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" 221 | ], 222 | "version": "==0.1.7" 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /02-notifon/autoscale_example.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import boto3 3 | session = boto3.Session(profile_name='pythonAutomation') 4 | as_client = session.client('autoscaling') 5 | as_client.describe_auto_scaling_groups() 6 | as_client.describe_policies() 7 | as_client.execute_policy(AutoScalingGroupName='Notifon Example Group', PolicyName='Scale Down') 8 | as_client.execute_policy(AutoScalingGroupName='Notifon Example Group', PolicyName='Scale Up') 9 | as_client.execute_policy(AutoScalingGroupName='Notifon Example Group', PolicyName='Scale Up') 10 | -------------------------------------------------------------------------------- /02-notifon/config.tmpl.json: -------------------------------------------------------------------------------- 1 | { 2 | "notifier": { 3 | "slack_webhook_url": "", 4 | "profile": "" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /02-notifon/ec2_example.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import boto3 3 | session = boto3.Session(profile_name='pythonAutomation') 4 | ec2 = session.resource('ec2') 5 | key_name = 'python_automation_key' 6 | key_path = key_name + '.pem' 7 | key = ec2.create_key_pair(KeyName=key_name) 8 | key.key_material 9 | with open(key_path, 'w') as key_file: 10 | key_file.write(key.key_material) 11 | 12 | get_ipython().run_line_magic('ls', '-l python_automation_key.pem') 13 | import os, stat 14 | os.chmod(key_path, stat.S_IRUSR | stat.S_IWUSR) 15 | get_ipython().run_line_magic('ls', '-l python_automation_key.pem') 16 | ec2.images.filter(Owners=['amazon']) 17 | list(ec2.images.filter(Owners=['amazon'])) 18 | len(list(ec2.images.filter(Owners=['amazon']))) 19 | img = ec2.Image('ami-922914f7') 20 | img.name 21 | ec2_apse2 = session.resource('ec2', region_name='ap-southeast-2') 22 | img_apse2 = ec2_apse2.Image('ami-922914f7') 23 | img_apse2.name 24 | img.name 25 | ami_name = 'amzn-ami-hvm-2018.03.0.20180508-x86_64-gp2' 26 | filters = [{'Name': 'name', 'Values': [ami_name]}] 27 | list(ec2.images.filter(Owners=['amazon'], Filters=filters)) 28 | list(ec2_apse2.images.filter(Owners=['amazon'], Filters=filters)) 29 | img 30 | key 31 | instances = ec2.create_instances(ImageId=img.id, MinCount=1, MaxCount=1, InstanceType='t2.micro', KeyName=key.key_name) 32 | instances 33 | ec2.Instance(id='i-0c8adb1a764565e54') 34 | inst = instances[0] 35 | inst.terminate() 36 | instances = ec2.create_instances(ImageId=img.id, MinCount=1, MaxCount=1, InstanceType='t2.micro', KeyName=key.key_name) 37 | inst= instances[0] 38 | inst.public_dns_name 39 | inst.wait_until_running() 40 | inst.reload() 41 | inst.public_dns_name 42 | inst.security_groups 43 | # Look up the security group 44 | # Authorize incoming connections from our public IP address, on port 22 (the port SSH uses) 45 | sg = ec2.SecurityGroup(inst.security_groups[0]['GroupId']) 46 | sg.authorize_ingress(IpPermissions=[{'FromPort': 22, 'ToPort': 22, 'IpProtocol': 'TCP', 'IpRanges': [{'CidrIp': '162.233.171.126/32'}]}]) 47 | sg.authorize_ingress(IpPermissions=[{'FromPort': 80, 'ToPort': 80, 'IpProtocol': 'TCP', 'IpRanges': [{'CidrIp': '0.0.0.0/0'}]}]) 48 | inst.public_dns_name 49 | get_ipython().run_line_magic('history', '') 50 | -------------------------------------------------------------------------------- /02-notifon/notifier/.gitignore: -------------------------------------------------------------------------------- 1 | # Distribution / packaging 2 | .Python 3 | env/ 4 | build/ 5 | develop-eggs/ 6 | dist/ 7 | downloads/ 8 | eggs/ 9 | .eggs/ 10 | lib/ 11 | lib64/ 12 | parts/ 13 | sdist/ 14 | var/ 15 | *.egg-info/ 16 | .installed.cfg 17 | *.egg 18 | 19 | # Serverless directories 20 | .serverless -------------------------------------------------------------------------------- /02-notifon/notifier/handler.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import requests 4 | 5 | def post_to_slack(event, context): 6 | slack_webhook_url = os.environ['SLACK_WEBHOOK_URL'] 7 | 8 | slack_message = "From {source} at {detail[StartTime]}: {detail[Description]}".format(**event) 9 | data = { "text": slack_message } 10 | requests.post(slack_webhook_url, json=data) 11 | 12 | return 13 | -------------------------------------------------------------------------------- /02-notifon/notifier/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notifon-notifier", 3 | "version": "0.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "balanced-match": { 8 | "version": "1.0.0", 9 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 10 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 11 | "dev": true 12 | }, 13 | "bluebird": { 14 | "version": "3.5.1", 15 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", 16 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", 17 | "dev": true 18 | }, 19 | "brace-expansion": { 20 | "version": "1.1.11", 21 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 22 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 23 | "dev": true, 24 | "requires": { 25 | "balanced-match": "1.0.0", 26 | "concat-map": "0.0.1" 27 | } 28 | }, 29 | "concat-map": { 30 | "version": "0.0.1", 31 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 32 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 33 | "dev": true 34 | }, 35 | "core-js": { 36 | "version": "2.3.0", 37 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", 38 | "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", 39 | "dev": true 40 | }, 41 | "core-util-is": { 42 | "version": "1.0.2", 43 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 44 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 45 | "dev": true 46 | }, 47 | "es6-promise": { 48 | "version": "3.0.2", 49 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", 50 | "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", 51 | "dev": true 52 | }, 53 | "fs-extra": { 54 | "version": "6.0.1", 55 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", 56 | "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", 57 | "dev": true, 58 | "requires": { 59 | "graceful-fs": "4.1.11", 60 | "jsonfile": "4.0.0", 61 | "universalify": "0.1.2" 62 | } 63 | }, 64 | "fs.realpath": { 65 | "version": "1.0.0", 66 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 67 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 68 | "dev": true 69 | }, 70 | "glob": { 71 | "version": "7.1.2", 72 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 73 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 74 | "dev": true, 75 | "requires": { 76 | "fs.realpath": "1.0.0", 77 | "inflight": "1.0.6", 78 | "inherits": "2.0.3", 79 | "minimatch": "3.0.4", 80 | "once": "1.4.0", 81 | "path-is-absolute": "1.0.1" 82 | } 83 | }, 84 | "glob-all": { 85 | "version": "3.1.0", 86 | "resolved": "https://registry.npmjs.org/glob-all/-/glob-all-3.1.0.tgz", 87 | "integrity": "sha1-iRPd+17hrHgSZWJBsD1SF8ZLAqs=", 88 | "dev": true, 89 | "requires": { 90 | "glob": "7.1.2", 91 | "yargs": "1.2.6" 92 | } 93 | }, 94 | "graceful-fs": { 95 | "version": "4.1.11", 96 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 97 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 98 | "dev": true 99 | }, 100 | "immediate": { 101 | "version": "3.0.6", 102 | "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", 103 | "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", 104 | "dev": true 105 | }, 106 | "inflight": { 107 | "version": "1.0.6", 108 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 109 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 110 | "dev": true, 111 | "requires": { 112 | "once": "1.4.0", 113 | "wrappy": "1.0.2" 114 | } 115 | }, 116 | "inherits": { 117 | "version": "2.0.3", 118 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 119 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 120 | "dev": true 121 | }, 122 | "is-wsl": { 123 | "version": "1.1.0", 124 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", 125 | "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", 126 | "dev": true 127 | }, 128 | "isarray": { 129 | "version": "1.0.0", 130 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 131 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 132 | "dev": true 133 | }, 134 | "jsonfile": { 135 | "version": "4.0.0", 136 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 137 | "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", 138 | "dev": true, 139 | "requires": { 140 | "graceful-fs": "4.1.11" 141 | } 142 | }, 143 | "jszip": { 144 | "version": "3.1.5", 145 | "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", 146 | "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", 147 | "dev": true, 148 | "requires": { 149 | "core-js": "2.3.0", 150 | "es6-promise": "3.0.2", 151 | "lie": "3.1.1", 152 | "pako": "1.0.6", 153 | "readable-stream": "2.0.6" 154 | } 155 | }, 156 | "lie": { 157 | "version": "3.1.1", 158 | "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", 159 | "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", 160 | "dev": true, 161 | "requires": { 162 | "immediate": "3.0.6" 163 | } 164 | }, 165 | "lodash.get": { 166 | "version": "4.4.2", 167 | "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", 168 | "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", 169 | "dev": true 170 | }, 171 | "lodash.set": { 172 | "version": "4.3.2", 173 | "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", 174 | "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", 175 | "dev": true 176 | }, 177 | "lodash.uniqby": { 178 | "version": "4.7.0", 179 | "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", 180 | "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=", 181 | "dev": true 182 | }, 183 | "lodash.values": { 184 | "version": "4.3.0", 185 | "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-4.3.0.tgz", 186 | "integrity": "sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=", 187 | "dev": true 188 | }, 189 | "minimatch": { 190 | "version": "3.0.4", 191 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 192 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 193 | "dev": true, 194 | "requires": { 195 | "brace-expansion": "1.1.11" 196 | } 197 | }, 198 | "minimist": { 199 | "version": "0.1.0", 200 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.1.0.tgz", 201 | "integrity": "sha1-md9lelJXTCHJBXSX33QnkLK0wN4=", 202 | "dev": true 203 | }, 204 | "once": { 205 | "version": "1.4.0", 206 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 207 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 208 | "dev": true, 209 | "requires": { 210 | "wrappy": "1.0.2" 211 | } 212 | }, 213 | "pako": { 214 | "version": "1.0.6", 215 | "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", 216 | "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", 217 | "dev": true 218 | }, 219 | "path-is-absolute": { 220 | "version": "1.0.1", 221 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 222 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 223 | "dev": true 224 | }, 225 | "process-nextick-args": { 226 | "version": "1.0.7", 227 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", 228 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", 229 | "dev": true 230 | }, 231 | "readable-stream": { 232 | "version": "2.0.6", 233 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", 234 | "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", 235 | "dev": true, 236 | "requires": { 237 | "core-util-is": "1.0.2", 238 | "inherits": "2.0.3", 239 | "isarray": "1.0.0", 240 | "process-nextick-args": "1.0.7", 241 | "string_decoder": "0.10.31", 242 | "util-deprecate": "1.0.2" 243 | } 244 | }, 245 | "rimraf": { 246 | "version": "2.6.2", 247 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 248 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 249 | "dev": true, 250 | "requires": { 251 | "glob": "7.1.2" 252 | } 253 | }, 254 | "serverless-python-requirements": { 255 | "version": "4.1.0", 256 | "resolved": "https://registry.npmjs.org/serverless-python-requirements/-/serverless-python-requirements-4.1.0.tgz", 257 | "integrity": "sha512-7Z3lUHmfy8DPOLqHMTrFJJQMOFpGfXO0EYYqnNCL7Z4SdBIF+B9kQdZvJoImyqa+stN5zuwwJ2s/4WZrWFN3dA==", 258 | "dev": true, 259 | "requires": { 260 | "bluebird": "3.5.1", 261 | "fs-extra": "6.0.1", 262 | "glob-all": "3.1.0", 263 | "is-wsl": "1.1.0", 264 | "jszip": "3.1.5", 265 | "lodash.get": "4.4.2", 266 | "lodash.set": "4.3.2", 267 | "lodash.uniqby": "4.7.0", 268 | "lodash.values": "4.3.0", 269 | "rimraf": "2.6.2" 270 | } 271 | }, 272 | "string_decoder": { 273 | "version": "0.10.31", 274 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 275 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 276 | "dev": true 277 | }, 278 | "universalify": { 279 | "version": "0.1.2", 280 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 281 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", 282 | "dev": true 283 | }, 284 | "util-deprecate": { 285 | "version": "1.0.2", 286 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 287 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 288 | "dev": true 289 | }, 290 | "wrappy": { 291 | "version": "1.0.2", 292 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 293 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 294 | "dev": true 295 | }, 296 | "yargs": { 297 | "version": "1.2.6", 298 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.2.6.tgz", 299 | "integrity": "sha1-nHtKgv1dWVsr8Xq23MQxNUMv40s=", 300 | "dev": true, 301 | "requires": { 302 | "minimist": "0.1.0" 303 | } 304 | } 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /02-notifon/notifier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notifon-notifier", 3 | "description": "", 4 | "version": "0.1.0", 5 | "dependencies": {}, 6 | "devDependencies": { 7 | "serverless-python-requirements": "^4.1.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /02-notifon/notifier/requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.18.4 2 | sodapy==1.4.6 3 | -------------------------------------------------------------------------------- /02-notifon/notifier/serverless.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Serverless! 2 | # 3 | # This file is the main config file for your service. 4 | # It's very minimal at this point and uses default values. 5 | # You can always add more config options for more control. 6 | # We've included some commented out config examples here. 7 | # Just uncomment any of them to get that config option. 8 | # 9 | # For full config options, check the docs: 10 | # docs.serverless.com 11 | # 12 | # Happy Coding! 13 | 14 | service: notifon-notifier 15 | 16 | # You can pin your service to only deploy with a specific Serverless version 17 | # Check out our docs for more details 18 | # frameworkVersion: "=X.X.X" 19 | 20 | provider: 21 | name: aws 22 | runtime: python3.6 23 | profile: ${file(../config.${self:provider.stage}.json):notifier.profile} 24 | region: us-east-2 25 | environment: 26 | SLACK_WEBHOOK_URL: ${file(../config.${self:provider.stage}.json):notifier.slack_webhook_url} 27 | 28 | # you can overwrite defaults here 29 | # stage: dev 30 | # region: us-east-1 31 | 32 | # you can add statements to the Lambda function's IAM Role here 33 | # iamRoleStatements: 34 | # - Effect: "Allow" 35 | # Action: 36 | # - "s3:ListBucket" 37 | # Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] } 38 | # - Effect: "Allow" 39 | # Action: 40 | # - "s3:PutObject" 41 | # Resource: 42 | # Fn::Join: 43 | # - "" 44 | # - - "arn:aws:s3:::" 45 | # - "Ref" : "ServerlessDeploymentBucket" 46 | # - "/*" 47 | 48 | # you can define service wide environment variables here 49 | # environment: 50 | # variable1: value1 51 | 52 | # you can add packaging information here 53 | #package: 54 | # include: 55 | # - include-me.py 56 | # - include-me-dir/** 57 | # exclude: 58 | # - exclude-me.py 59 | # - exclude-me-dir/** 60 | 61 | functions: 62 | post-to-slack: 63 | handler: handler.post_to_slack 64 | events: 65 | - cloudwatchEvent: 66 | event: 67 | source: 68 | - "aws.autoscaling" 69 | detail-type: 70 | - "EC2 Instance Launch Successful" 71 | detail: 72 | AutoScalingGroupName: 73 | - "Notifon Example Group" 74 | 75 | 76 | 77 | # The following are a few example events you can configure 78 | # NOTE: Please make sure to change your handler code to work with those events 79 | # Check the event documentation for details 80 | # events: 81 | # - http: 82 | # path: users/create 83 | # method: get 84 | # - s3: ${env:BUCKET} 85 | # - schedule: rate(10 minutes) 86 | # - sns: greeter-topic 87 | # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 88 | # - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx 89 | # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx 90 | # - iot: 91 | # sql: "SELECT * FROM 'some_topic'" 92 | # - cloudwatchEvent: 93 | # event: 94 | # source: 95 | # - "aws.ec2" 96 | # detail-type: 97 | # - "EC2 Instance State-change Notification" 98 | # detail: 99 | # state: 100 | # - pending 101 | # - cloudwatchLog: '/aws/lambda/hello' 102 | # - cognitoUserPool: 103 | # pool: MyUserPool 104 | # trigger: PreSignUp 105 | 106 | # Define function environment variables here 107 | # environment: 108 | # variable2: value2 109 | 110 | # you can add CloudFormation resource templates here 111 | #resources: 112 | # Resources: 113 | # NewResource: 114 | # Type: AWS::S3::Bucket 115 | # Properties: 116 | # BucketName: my-new-bucket 117 | # Outputs: 118 | # NewOutput: 119 | # Description: "Description for the output" 120 | # Value: "Some output value" 121 | 122 | plugins: 123 | - serverless-python-requirements 124 | -------------------------------------------------------------------------------- /02-notifon/notifier/setup.cfg: -------------------------------------------------------------------------------- 1 | [install] 2 | prefix= 3 | -------------------------------------------------------------------------------- /02-notifon/post_to_slack.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import requests 3 | url = '' # Replace with slack webhook URL 4 | data = { "text": "Hello, world." } 5 | requests.post(url, json=data) 6 | -------------------------------------------------------------------------------- /02-notifon/scale-up.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import boto3 3 | session = boto3.Session(profile_name='pythonAutomation') 4 | as_client = session.client('autoscaling') 5 | 6 | as_client.execute_policy(AutoScalingGroupName='Notifon Example Group', PolicyName='Scale Up') 7 | -------------------------------------------------------------------------------- /03-videolyzer/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | "boto3" = "*" 8 | click = "*" 9 | 10 | [dev-packages] 11 | ipython = "*" 12 | 13 | [requires] 14 | python_version = "3.6" 15 | -------------------------------------------------------------------------------- /03-videolyzer/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "d67aa3537892442cc07dbe1b363f63d25199d5241a0073642f38f406e7a97966" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.6" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "boto3": { 20 | "hashes": [ 21 | "sha256:43255355cb4ad0fcba178658073120107695a6258259d4a5d387c0cb8354b928", 22 | "sha256:d5413b68157d73515839c6b1bbc00c0a9b0722ce8b14475215cf68984003f82b" 23 | ], 24 | "index": "pypi", 25 | "version": "==1.7.52" 26 | }, 27 | "botocore": { 28 | "hashes": [ 29 | "sha256:5e809e326564d1b6422b0c7235300da428601e6694c8e7d7f05e38329fe40e31", 30 | "sha256:a4acb24c8ba2fb148e41b385e8acd10c5d96bf576ad22be1364919bed6f74f4e" 31 | ], 32 | "version": "==1.10.52" 33 | }, 34 | "click": { 35 | "hashes": [ 36 | "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", 37 | "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" 38 | ], 39 | "index": "pypi", 40 | "version": "==6.7" 41 | }, 42 | "docutils": { 43 | "hashes": [ 44 | "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", 45 | "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", 46 | "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6" 47 | ], 48 | "version": "==0.14" 49 | }, 50 | "jmespath": { 51 | "hashes": [ 52 | "sha256:6a81d4c9aa62caf061cb517b4d9ad1dd300374cd4706997aff9cd6aedd61fc64", 53 | "sha256:f11b4461f425740a1d908e9a3f7365c3d2e569f6ca68a2ff8bc5bcd9676edd63" 54 | ], 55 | "version": "==0.9.3" 56 | }, 57 | "python-dateutil": { 58 | "hashes": [ 59 | "sha256:1adb80e7a782c12e52ef9a8182bebeb73f1d7e24e374397af06fb4956c8dc5c0", 60 | "sha256:e27001de32f627c22380a688bcc43ce83504a7bc5da472209b4c70f02829f0b8" 61 | ], 62 | "markers": "python_version >= '2.7'", 63 | "version": "==2.7.3" 64 | }, 65 | "s3transfer": { 66 | "hashes": [ 67 | "sha256:90dc18e028989c609146e241ea153250be451e05ecc0c2832565231dacdf59c1", 68 | "sha256:c7a9ec356982d5e9ab2d4b46391a7d6a950e2b04c472419f5fdec70cc0ada72f" 69 | ], 70 | "version": "==0.1.13" 71 | }, 72 | "six": { 73 | "hashes": [ 74 | "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", 75 | "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" 76 | ], 77 | "version": "==1.11.0" 78 | } 79 | }, 80 | "develop": { 81 | "appnope": { 82 | "hashes": [ 83 | "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", 84 | "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71" 85 | ], 86 | "markers": "sys_platform == 'darwin'", 87 | "version": "==0.1.0" 88 | }, 89 | "backcall": { 90 | "hashes": [ 91 | "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", 92 | "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2" 93 | ], 94 | "version": "==0.1.0" 95 | }, 96 | "decorator": { 97 | "hashes": [ 98 | "sha256:2c51dff8ef3c447388fe5e4453d24a2bf128d3a4c32af3fabef1f01c6851ab82", 99 | "sha256:c39efa13fbdeb4506c476c9b3babf6a718da943dab7811c206005a4a956c080c" 100 | ], 101 | "version": "==4.3.0" 102 | }, 103 | "ipython": { 104 | "hashes": [ 105 | "sha256:a0c96853549b246991046f32d19db7140f5b1a644cc31f0dc1edc86713b7676f", 106 | "sha256:eca537aa61592aca2fef4adea12af8e42f5c335004dfa80c78caf80e8b525e5c" 107 | ], 108 | "index": "pypi", 109 | "version": "==6.4.0" 110 | }, 111 | "ipython-genutils": { 112 | "hashes": [ 113 | "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", 114 | "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" 115 | ], 116 | "version": "==0.2.0" 117 | }, 118 | "jedi": { 119 | "hashes": [ 120 | "sha256:b409ed0f6913a701ed474a614a3bb46e6953639033e31f769ca7581da5bd1ec1", 121 | "sha256:c254b135fb39ad76e78d4d8f92765ebc9bf92cbc76f49e97ade1d5f5121e1f6f" 122 | ], 123 | "version": "==0.12.1" 124 | }, 125 | "parso": { 126 | "hashes": [ 127 | "sha256:8105449d86d858e53ce3e0044ede9dd3a395b1c9716c696af8aa3787158ab806", 128 | "sha256:d250235e52e8f9fc5a80cc2a5f804c9fefd886b2e67a2b1099cf085f403f8e33" 129 | ], 130 | "version": "==0.3.0" 131 | }, 132 | "pexpect": { 133 | "hashes": [ 134 | "sha256:2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba", 135 | "sha256:3fbd41d4caf27fa4a377bfd16fef87271099463e6fa73e92a52f92dfee5d425b" 136 | ], 137 | "markers": "sys_platform != 'win32'", 138 | "version": "==4.6.0" 139 | }, 140 | "pickleshare": { 141 | "hashes": [ 142 | "sha256:84a9257227dfdd6fe1b4be1319096c20eb85ff1e82c7932f36efccfe1b09737b", 143 | "sha256:c9a2541f25aeabc070f12f452e1f2a8eae2abd51e1cd19e8430402bdf4c1d8b5" 144 | ], 145 | "version": "==0.7.4" 146 | }, 147 | "prompt-toolkit": { 148 | "hashes": [ 149 | "sha256:1df952620eccb399c53ebb359cc7d9a8d3a9538cb34c5a1344bdbeb29fbcc381", 150 | "sha256:3f473ae040ddaa52b52f97f6b4a493cfa9f5920c255a12dc56a7d34397a398a4", 151 | "sha256:858588f1983ca497f1cf4ffde01d978a3ea02b01c8a26a8bbc5cd2e66d816917" 152 | ], 153 | "version": "==1.0.15" 154 | }, 155 | "ptyprocess": { 156 | "hashes": [ 157 | "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", 158 | "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f" 159 | ], 160 | "version": "==0.6.0" 161 | }, 162 | "pygments": { 163 | "hashes": [ 164 | "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d", 165 | "sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc" 166 | ], 167 | "version": "==2.2.0" 168 | }, 169 | "simplegeneric": { 170 | "hashes": [ 171 | "sha256:dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173" 172 | ], 173 | "version": "==0.8.1" 174 | }, 175 | "six": { 176 | "hashes": [ 177 | "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", 178 | "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" 179 | ], 180 | "version": "==1.11.0" 181 | }, 182 | "traitlets": { 183 | "hashes": [ 184 | "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", 185 | "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9" 186 | ], 187 | "version": "==4.3.2" 188 | }, 189 | "wcwidth": { 190 | "hashes": [ 191 | "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", 192 | "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" 193 | ], 194 | "version": "==0.1.7" 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /03-videolyzer/config.tmpl.json: -------------------------------------------------------------------------------- 1 | { 2 | "videolyzer": { 3 | "profile": "", 4 | "videos_bucket": "", 5 | "videos_table": "" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /03-videolyzer/handle-sns-event-example.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | event = {'Records': [{'EventSource': 'aws:sns', 'EventVersion': '1.0', 'EventSubscriptionArn': 'arn:aws:sns:us-east-1:426176885551:handleLabelDetectionTopic:01478983-30d7-47c7-ac3b-8c4d8fe17af4', 'Sns': {'Type': 'Notification', 'MessageId': '49ce09ca-3d12-5d25-af88-5a74cf680e70', 'TopicArn': 'arn:aws:sns:us-east-1:426176885551:handleLabelDetectionTopic', 'Subject': None, 'Message': '{"JobId":"dbd80e840fdec0f1525764d05f6b81166b8a318a7f8df6e55d2aa24d2eeaf443","Status":"SUCCEEDED","API":"StartLabelDetection","Timestamp":1531244602671,"Video":{"S3ObjectName":"Pexels Videos 4640.mp4","S3Bucket":"robinvideolyzervideos12345"}}', 'Timestamp': '2018-07-10T17:43:22.773Z', 'SignatureVersion': '1', 'Signature': 'Xg7bTkMLIXc9C1lPDLP0vaGDtSAiqJ7TL5+XABeq5RMVATkS4o2isvW5I5S8GG5aKhs2wj5aHC1wDekM8tbHSJpAFGlprN6tehgJRvCQkr9XVplgLxm8ACbw04HZskXTXd+s8Ay1Vu7a54tRfPEQ6GtuE1GJXLA6P+JtnXDV5gyrJDR87dwcHDvkajwlOhyoF8J+LJ8JmQvbNJlfu4ijxDLh0SBbaiFED7SRp1FKMjmzHD8wPshTSzfUBwUdhFFJueHiwM+eZjxR9zA+b+kywPPPjuRndBs7DgygbMVHs/6R6sUdbUzrBet7N5BwBlkXxLJb1aNF1bMXXK6qAidW9w==', 'SigningCertUrl': 'https://sns.us-east-1.amazonaws.com/SimpleNotificationService-eaea6120e66ea12e88dcd8bcbddca752.pem', 'UnsubscribeUrl': 'https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:426176885551:handleLabelDetectionTopic:01478983-30d7-47c7-ac3b-8c4d8fe17af4', 'MessageAttributes': {}}}]} 3 | event.keys() 4 | event['Records'][0] 5 | event['Records'][0].keys() 6 | event['Records'][0]['EventSource'] 7 | event['Records'][0]['EventVersion'] 8 | event['Records'][0]['EventSubscriptionArn'] 9 | event['Records'][0]['Sns'] 10 | event['Records'][0]['Sns']['Message'] 11 | event['Records'][0]['Sns']['Message']['JobId'] 12 | type(event['Records'][0]['Sns']['Message']) 13 | import json 14 | json.loads(event['Records'][0]['Sns']['Message']) 15 | -------------------------------------------------------------------------------- /03-videolyzer/label-detection.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import boto3 3 | session = boto3.Session(profile_name='pythonAutomation') 4 | s3 = session.resource('s3') 5 | bucket = s3.create_bucket(Bucket='robinvideolyzervideos') 6 | bucket = s3.create_bucket(Bucket='robinvideolyzervideos', LocationConstraint=session.region_name) 7 | bucket = s3.create_bucket(Bucket='robinvideolyzervideos') 8 | bucket = s3.create_bucket(Bucket='robinvideolyzervideos', CreateBucketConfiguration={'LocationConstraint': session.region_name}) 9 | from pathlib import Path 10 | get_ipython().run_line_magic('ls', '/Users/rnorwood/Downloads/*.mp4') 11 | pathname = '~/Downloads/Blurry Video Of People Working.mp4' 12 | path = Path(pathname).expanduser().resolve() 13 | print(path) 14 | bucket.upload_file(str(path), str(path.name)) 15 | rekognition_client = session.client('rekognition') 16 | response = rekognition_client.start_label_detection(Video={'S3Object': { 'Bucket': bucket.name, 'Name': path.name}}) 17 | response 18 | job_id = response['JobId'] 19 | result = rekognition_client.get_label_detection(JobId=job_id) 20 | result 21 | result.keys() 22 | result['JobStatus'] 23 | result['ResponseMetadata'] 24 | result['VideoMetadata'] 25 | result['Labels'] 26 | len(result['Labels']) 27 | -------------------------------------------------------------------------------- /03-videolyzer/s3-event-example.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | event = {'Records': [{'eventVersion': '2.0', 'eventSource': 'aws:s3', 'awsRegion': 'us-east-1', 'eventTime': '2018-07-09T14:47:06.505Z', 'eventName': 'ObjectCreated:CompleteMultipartUpload', 'userIdentity': {'principalId': 'AWS:AIDAITRTGNM64QPYU6DW6'}, 'requestParameters': {'sourceIPAddress': '162.233.171.126'}, 'responseElements': {'x-amz-request-id': '5C55894ABB4C09C1', 'x-amz-id-2': 'dBf64puCLEmiN6tkkky037+7JPSJj9n3xvJWDhptJ2VftvVWRgSn1GWhLl2CJFP1FSLYthwF3dc='}, 's3': {'s3SchemaVersion': '1.0', 'configurationId': '4256d959-8f6f-42c0-8a32-5036a840e2b0', 'bucket': {'name': 'robinvideolyzervideos12345', 'ownerIdentity': {'principalId': 'A3EGVT21L2CFMG'}, 'arn': 'arn:aws:s3:::robinvideolyzervideos12345'}, 'object': {'key': 'Pexels+Videos+4640.mp4', 'size': 10278716, 'eTag': '6bcf2e76121ff9d1c3bf616c49650be7-2', 'sequencer': '005B437568838B6599'}}}]} 3 | event 4 | event['Records'][0] 5 | event['Records'][0]['s3']['bucket']['name'] 6 | event['Records'][0]['s3']['object']['key'] 7 | import urllib 8 | urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key']) 9 | -------------------------------------------------------------------------------- /03-videolyzer/upload-file.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import click 4 | import boto3 5 | 6 | @click.option('--profile', default=None, help="Use a given AWS profile") 7 | @click.argument('pathname', type=click.Path(exists=True)) 8 | @click.argument('bucketname') 9 | @click.command() 10 | def upload_file(profile, pathname, bucketname): 11 | """Upload to """ 12 | 13 | session_cfg = {} 14 | if profile: 15 | session_cfg['profile_name'] = profile 16 | 17 | session = boto3.Session(**session_cfg) 18 | s3 = session.resource('s3') 19 | 20 | bucket = s3.Bucket(bucketname) 21 | path = Path(pathname).expanduser().resolve() 22 | 23 | bucket.upload_file(str(path), str(path.name)) 24 | 25 | if __name__ == '__main__': 26 | upload_file() 27 | 28 | -------------------------------------------------------------------------------- /03-videolyzer/videolyzer/.gitignore: -------------------------------------------------------------------------------- 1 | # Distribution / packaging 2 | .Python 3 | env/ 4 | build/ 5 | develop-eggs/ 6 | dist/ 7 | downloads/ 8 | eggs/ 9 | .eggs/ 10 | lib/ 11 | lib64/ 12 | parts/ 13 | sdist/ 14 | var/ 15 | *.egg-info/ 16 | .installed.cfg 17 | *.egg 18 | 19 | # Serverless directories 20 | .serverless -------------------------------------------------------------------------------- /03-videolyzer/videolyzer/handler.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import urllib 4 | 5 | import boto3 6 | 7 | def start_label_detection(bucket, key): 8 | rekognition_client = boto3.client('rekognition') 9 | response = rekognition_client.start_label_detection( 10 | Video={ 11 | 'S3Object': { 12 | 'Bucket': bucket, 13 | 'Name': key 14 | } 15 | }, 16 | NotificationChannel={ 17 | 'SNSTopicArn': os.environ['REKOGNITION_SNS_TOPIC_ARN'], 18 | 'RoleArn': os.environ['REKOGNITION_ROLE_ARN'] 19 | }) 20 | 21 | return 22 | 23 | def get_video_labels(job_id): 24 | rekognition_client = boto3.client('rekognition') 25 | 26 | response = rekognition_client.get_label_detection(JobId=job_id) 27 | 28 | next_token = response.get('NextToken', None) 29 | 30 | while next_token: 31 | next_page = rekognition_client.get_label_detection( 32 | JobId=job_id, 33 | NextToken=next_token 34 | ) 35 | 36 | next_token = next_page.get('NextToken', None) 37 | 38 | response['Labels'].extend(next_page['Labels']) 39 | 40 | return response 41 | 42 | def make_item(data): 43 | if isinstance(data, dict): 44 | return { k: make_item(v) for k, v in data.items() } 45 | 46 | if isinstance(data, list): 47 | return [ make_item(v) for v in data ] 48 | 49 | if isinstance(data, float): 50 | return str(data) 51 | 52 | return data 53 | 54 | def put_labels_in_db(data, video_name, video_bucket): 55 | del data['ResponseMetadata'] 56 | del data['JobStatus'] 57 | 58 | data['videoName'] = video_name 59 | data['videoBucket'] = video_bucket 60 | 61 | dynamodb = boto3.resource('dynamodb') 62 | table_name = os.environ['DYNAMODB_TABLE_NAME'] 63 | videos_table = dynamodb.Table(table_name) 64 | 65 | data = make_item(data) 66 | 67 | videos_table.put_item(Item=data) 68 | 69 | return 70 | 71 | # Lambda events 72 | 73 | def start_processing_video(event, context): 74 | for record in event['Records']: 75 | start_label_detection( 76 | record['s3']['bucket']['name'], 77 | urllib.parse.unquote_plus(record['s3']['object']['key']) 78 | ) 79 | 80 | 81 | return 82 | 83 | def handle_label_detection(event, context): 84 | for record in event['Records']: 85 | message = json.loads(record['Sns']['Message']) 86 | job_id = message['JobId'] 87 | s3_object = message['Video']['S3ObjectName'] 88 | s3_bucket = message['Video']['S3Bucket'] 89 | 90 | response = get_video_labels(job_id) 91 | put_labels_in_db(response, s3_object, s3_bucket) 92 | 93 | return 94 | -------------------------------------------------------------------------------- /03-videolyzer/videolyzer/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "@serverless/fdk": { 6 | "version": "0.5.1", 7 | "resolved": "https://registry.npmjs.org/@serverless/fdk/-/fdk-0.5.1.tgz", 8 | "integrity": "sha512-Z/+5R0AohLwDT1E+9BTeUA7NozlyIoTh0iEt6x8x+ZZhIBK5HBMBN6v2LfkI4wmmOOyceTvsN0l8nWfGp4Oh5g==", 9 | "requires": { 10 | "isomorphic-fetch": "2.2.1", 11 | "ramda": "0.24.1", 12 | "url-parse": "1.4.1" 13 | } 14 | }, 15 | "@serverless/platform-sdk": { 16 | "version": "0.1.5", 17 | "resolved": "https://registry.npmjs.org/@serverless/platform-sdk/-/platform-sdk-0.1.5.tgz", 18 | "integrity": "sha512-d61v7iSAqvNlT+9TBZnsJwmSmVtu6MERwa+IUTHj46RwICPYsup3dB0U4jy3mhTAk1XtQ+EWuUOSTZdck2aD8w==", 19 | "requires": { 20 | "babel-polyfill": "6.26.0", 21 | "bluebird": "3.5.1", 22 | "body-parser": "1.18.3", 23 | "chalk": "2.4.1", 24 | "cors": "2.8.4", 25 | "express": "4.16.3", 26 | "is-docker": "1.1.0", 27 | "is-wsl": "1.1.0", 28 | "isomorphic-fetch": "2.2.1", 29 | "node-fetch": "2.1.2", 30 | "opn": "5.3.0", 31 | "querystring": "0.2.0", 32 | "ramda": "0.25.0", 33 | "source-map-support": "0.5.6" 34 | }, 35 | "dependencies": { 36 | "node-fetch": { 37 | "version": "2.1.2", 38 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", 39 | "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=" 40 | }, 41 | "ramda": { 42 | "version": "0.25.0", 43 | "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.25.0.tgz", 44 | "integrity": "sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==" 45 | } 46 | } 47 | }, 48 | "accepts": { 49 | "version": "1.3.5", 50 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 51 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 52 | "requires": { 53 | "mime-types": "2.1.18", 54 | "negotiator": "0.6.1" 55 | } 56 | }, 57 | "agent-base": { 58 | "version": "4.2.1", 59 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", 60 | "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", 61 | "requires": { 62 | "es6-promisify": "5.0.0" 63 | } 64 | }, 65 | "ansi": { 66 | "version": "0.3.1", 67 | "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", 68 | "integrity": "sha1-DELU+xcWDVqa8eSEus4cZpIsGyE=" 69 | }, 70 | "ansi-align": { 71 | "version": "2.0.0", 72 | "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", 73 | "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", 74 | "requires": { 75 | "string-width": "2.1.1" 76 | }, 77 | "dependencies": { 78 | "ansi-regex": { 79 | "version": "3.0.0", 80 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 81 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 82 | }, 83 | "is-fullwidth-code-point": { 84 | "version": "2.0.0", 85 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 86 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 87 | }, 88 | "string-width": { 89 | "version": "2.1.1", 90 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 91 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 92 | "requires": { 93 | "is-fullwidth-code-point": "2.0.0", 94 | "strip-ansi": "4.0.0" 95 | } 96 | }, 97 | "strip-ansi": { 98 | "version": "4.0.0", 99 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 100 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 101 | "requires": { 102 | "ansi-regex": "3.0.0" 103 | } 104 | } 105 | } 106 | }, 107 | "ansi-escapes": { 108 | "version": "1.4.0", 109 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", 110 | "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=" 111 | }, 112 | "ansi-regex": { 113 | "version": "2.1.1", 114 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 115 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 116 | }, 117 | "ansi-styles": { 118 | "version": "3.2.1", 119 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 120 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 121 | "requires": { 122 | "color-convert": "1.9.2" 123 | } 124 | }, 125 | "archiver": { 126 | "version": "1.3.0", 127 | "resolved": "https://registry.npmjs.org/archiver/-/archiver-1.3.0.tgz", 128 | "integrity": "sha1-TyGU1tj5nfP1MeaIHxTxXVX6ryI=", 129 | "requires": { 130 | "archiver-utils": "1.3.0", 131 | "async": "2.6.1", 132 | "buffer-crc32": "0.2.13", 133 | "glob": "7.1.2", 134 | "lodash": "4.17.10", 135 | "readable-stream": "2.3.6", 136 | "tar-stream": "1.6.1", 137 | "walkdir": "0.0.11", 138 | "zip-stream": "1.2.0" 139 | }, 140 | "dependencies": { 141 | "async": { 142 | "version": "2.6.1", 143 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", 144 | "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", 145 | "requires": { 146 | "lodash": "4.17.10" 147 | } 148 | } 149 | } 150 | }, 151 | "archiver-utils": { 152 | "version": "1.3.0", 153 | "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-1.3.0.tgz", 154 | "integrity": "sha1-5QtMCccL89aA4y/xt5lOn52JUXQ=", 155 | "requires": { 156 | "glob": "7.1.2", 157 | "graceful-fs": "4.1.11", 158 | "lazystream": "1.0.0", 159 | "lodash": "4.17.10", 160 | "normalize-path": "2.1.1", 161 | "readable-stream": "2.3.6" 162 | } 163 | }, 164 | "are-we-there-yet": { 165 | "version": "1.1.5", 166 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", 167 | "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", 168 | "requires": { 169 | "delegates": "1.0.0", 170 | "readable-stream": "2.3.6" 171 | } 172 | }, 173 | "argparse": { 174 | "version": "1.0.10", 175 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 176 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 177 | "requires": { 178 | "sprintf-js": "1.0.3" 179 | } 180 | }, 181 | "array-flatten": { 182 | "version": "1.1.1", 183 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 184 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 185 | }, 186 | "array-union": { 187 | "version": "1.0.2", 188 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 189 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", 190 | "requires": { 191 | "array-uniq": "1.0.3" 192 | } 193 | }, 194 | "array-uniq": { 195 | "version": "1.0.3", 196 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 197 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" 198 | }, 199 | "async": { 200 | "version": "1.5.2", 201 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 202 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" 203 | }, 204 | "asynckit": { 205 | "version": "0.4.0", 206 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 207 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 208 | }, 209 | "aws-sdk": { 210 | "version": "2.270.1", 211 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.270.1.tgz", 212 | "integrity": "sha512-8PrzmogkqU/G2/b9u5ktyPMcrL9dIeYVNZu0LW0N2EwIb/V0W8gQ+nNm6A6EEF4T0mkvT38QwNSqlFfNCZYcBg==", 213 | "requires": { 214 | "buffer": "4.9.1", 215 | "events": "1.1.1", 216 | "ieee754": "1.1.8", 217 | "jmespath": "0.15.0", 218 | "querystring": "0.2.0", 219 | "sax": "1.2.1", 220 | "url": "0.10.3", 221 | "uuid": "3.1.0", 222 | "xml2js": "0.4.17" 223 | }, 224 | "dependencies": { 225 | "uuid": { 226 | "version": "3.1.0", 227 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", 228 | "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" 229 | } 230 | } 231 | }, 232 | "babel-polyfill": { 233 | "version": "6.26.0", 234 | "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", 235 | "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", 236 | "requires": { 237 | "babel-runtime": "6.26.0", 238 | "core-js": "2.5.7", 239 | "regenerator-runtime": "0.10.5" 240 | } 241 | }, 242 | "babel-runtime": { 243 | "version": "6.26.0", 244 | "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", 245 | "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", 246 | "requires": { 247 | "core-js": "2.5.7", 248 | "regenerator-runtime": "0.11.1" 249 | }, 250 | "dependencies": { 251 | "regenerator-runtime": { 252 | "version": "0.11.1", 253 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", 254 | "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" 255 | } 256 | } 257 | }, 258 | "balanced-match": { 259 | "version": "1.0.0", 260 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 261 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 262 | }, 263 | "base64-js": { 264 | "version": "1.3.0", 265 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", 266 | "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" 267 | }, 268 | "bl": { 269 | "version": "1.2.2", 270 | "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", 271 | "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", 272 | "requires": { 273 | "readable-stream": "2.3.6", 274 | "safe-buffer": "5.1.1" 275 | } 276 | }, 277 | "bluebird": { 278 | "version": "3.5.1", 279 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", 280 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" 281 | }, 282 | "body-parser": { 283 | "version": "1.18.3", 284 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 285 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 286 | "requires": { 287 | "bytes": "3.0.0", 288 | "content-type": "1.0.4", 289 | "debug": "2.6.9", 290 | "depd": "1.1.2", 291 | "http-errors": "1.6.3", 292 | "iconv-lite": "0.4.23", 293 | "on-finished": "2.3.0", 294 | "qs": "6.5.2", 295 | "raw-body": "2.3.3", 296 | "type-is": "1.6.16" 297 | } 298 | }, 299 | "boxen": { 300 | "version": "1.3.0", 301 | "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", 302 | "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", 303 | "requires": { 304 | "ansi-align": "2.0.0", 305 | "camelcase": "4.1.0", 306 | "chalk": "2.4.1", 307 | "cli-boxes": "1.0.0", 308 | "string-width": "2.1.1", 309 | "term-size": "1.2.0", 310 | "widest-line": "2.0.0" 311 | }, 312 | "dependencies": { 313 | "ansi-regex": { 314 | "version": "3.0.0", 315 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 316 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 317 | }, 318 | "is-fullwidth-code-point": { 319 | "version": "2.0.0", 320 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 321 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 322 | }, 323 | "string-width": { 324 | "version": "2.1.1", 325 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 326 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 327 | "requires": { 328 | "is-fullwidth-code-point": "2.0.0", 329 | "strip-ansi": "4.0.0" 330 | } 331 | }, 332 | "strip-ansi": { 333 | "version": "4.0.0", 334 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 335 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 336 | "requires": { 337 | "ansi-regex": "3.0.0" 338 | } 339 | } 340 | } 341 | }, 342 | "brace-expansion": { 343 | "version": "1.1.11", 344 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 345 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 346 | "requires": { 347 | "balanced-match": "1.0.0", 348 | "concat-map": "0.0.1" 349 | } 350 | }, 351 | "buffer": { 352 | "version": "4.9.1", 353 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", 354 | "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", 355 | "requires": { 356 | "base64-js": "1.3.0", 357 | "ieee754": "1.1.8", 358 | "isarray": "1.0.0" 359 | } 360 | }, 361 | "buffer-alloc": { 362 | "version": "1.2.0", 363 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", 364 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", 365 | "requires": { 366 | "buffer-alloc-unsafe": "1.1.0", 367 | "buffer-fill": "1.0.0" 368 | } 369 | }, 370 | "buffer-alloc-unsafe": { 371 | "version": "1.1.0", 372 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", 373 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" 374 | }, 375 | "buffer-crc32": { 376 | "version": "0.2.13", 377 | "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", 378 | "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" 379 | }, 380 | "buffer-fill": { 381 | "version": "1.0.0", 382 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", 383 | "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" 384 | }, 385 | "buffer-from": { 386 | "version": "1.1.0", 387 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", 388 | "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==" 389 | }, 390 | "bytes": { 391 | "version": "3.0.0", 392 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 393 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 394 | }, 395 | "camelcase": { 396 | "version": "4.1.0", 397 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", 398 | "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" 399 | }, 400 | "capture-stack-trace": { 401 | "version": "1.0.0", 402 | "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", 403 | "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=" 404 | }, 405 | "caw": { 406 | "version": "2.0.1", 407 | "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", 408 | "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", 409 | "requires": { 410 | "get-proxy": "2.1.0", 411 | "isurl": "1.0.0", 412 | "tunnel-agent": "0.6.0", 413 | "url-to-options": "1.0.1" 414 | } 415 | }, 416 | "chalk": { 417 | "version": "2.4.1", 418 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 419 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 420 | "requires": { 421 | "ansi-styles": "3.2.1", 422 | "escape-string-regexp": "1.0.5", 423 | "supports-color": "5.4.0" 424 | } 425 | }, 426 | "ci-info": { 427 | "version": "1.1.3", 428 | "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz", 429 | "integrity": "sha512-SK/846h/Rcy8q9Z9CAwGBLfCJ6EkjJWdpelWDufQpqVDYq2Wnnv8zlSO6AMQap02jvhVruKKpEtQOufo3pFhLg==" 430 | }, 431 | "cli-boxes": { 432 | "version": "1.0.0", 433 | "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", 434 | "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" 435 | }, 436 | "cli-cursor": { 437 | "version": "1.0.2", 438 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", 439 | "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", 440 | "requires": { 441 | "restore-cursor": "1.0.1" 442 | } 443 | }, 444 | "cli-width": { 445 | "version": "2.2.0", 446 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", 447 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" 448 | }, 449 | "code-point-at": { 450 | "version": "1.1.0", 451 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 452 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 453 | }, 454 | "color-convert": { 455 | "version": "1.9.2", 456 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", 457 | "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", 458 | "requires": { 459 | "color-name": "1.1.1" 460 | } 461 | }, 462 | "color-name": { 463 | "version": "1.1.1", 464 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", 465 | "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" 466 | }, 467 | "combined-stream": { 468 | "version": "1.0.6", 469 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", 470 | "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", 471 | "requires": { 472 | "delayed-stream": "1.0.0" 473 | } 474 | }, 475 | "commander": { 476 | "version": "2.8.1", 477 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", 478 | "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", 479 | "requires": { 480 | "graceful-readlink": "1.0.1" 481 | } 482 | }, 483 | "component-emitter": { 484 | "version": "1.2.1", 485 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", 486 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" 487 | }, 488 | "compress-commons": { 489 | "version": "1.2.2", 490 | "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.2.tgz", 491 | "integrity": "sha1-UkqfEJA/OoEzibAiXSfEi7dRiQ8=", 492 | "requires": { 493 | "buffer-crc32": "0.2.13", 494 | "crc32-stream": "2.0.0", 495 | "normalize-path": "2.1.1", 496 | "readable-stream": "2.3.6" 497 | } 498 | }, 499 | "concat-map": { 500 | "version": "0.0.1", 501 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 502 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 503 | }, 504 | "concat-stream": { 505 | "version": "1.6.2", 506 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 507 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 508 | "requires": { 509 | "buffer-from": "1.1.0", 510 | "inherits": "2.0.3", 511 | "readable-stream": "2.3.6", 512 | "typedarray": "0.0.6" 513 | } 514 | }, 515 | "config-chain": { 516 | "version": "1.1.11", 517 | "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.11.tgz", 518 | "integrity": "sha1-q6CXR9++TD5w52am5BWG4YWfxvI=", 519 | "requires": { 520 | "ini": "1.3.5", 521 | "proto-list": "1.2.4" 522 | } 523 | }, 524 | "configstore": { 525 | "version": "3.1.2", 526 | "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", 527 | "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", 528 | "requires": { 529 | "dot-prop": "4.2.0", 530 | "graceful-fs": "4.1.11", 531 | "make-dir": "1.3.0", 532 | "unique-string": "1.0.0", 533 | "write-file-atomic": "2.3.0", 534 | "xdg-basedir": "3.0.0" 535 | } 536 | }, 537 | "content-disposition": { 538 | "version": "0.5.2", 539 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 540 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 541 | }, 542 | "content-type": { 543 | "version": "1.0.4", 544 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 545 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 546 | }, 547 | "cookie": { 548 | "version": "0.3.1", 549 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 550 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 551 | }, 552 | "cookie-signature": { 553 | "version": "1.0.6", 554 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 555 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 556 | }, 557 | "cookiejar": { 558 | "version": "2.1.2", 559 | "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", 560 | "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" 561 | }, 562 | "core-js": { 563 | "version": "2.5.7", 564 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", 565 | "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" 566 | }, 567 | "core-util-is": { 568 | "version": "1.0.2", 569 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 570 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 571 | }, 572 | "cors": { 573 | "version": "2.8.4", 574 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", 575 | "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", 576 | "requires": { 577 | "object-assign": "4.1.1", 578 | "vary": "1.1.2" 579 | } 580 | }, 581 | "crc": { 582 | "version": "3.5.0", 583 | "resolved": "https://registry.npmjs.org/crc/-/crc-3.5.0.tgz", 584 | "integrity": "sha1-mLi6fUiWZbo5efWbITgTdBAaGWQ=" 585 | }, 586 | "crc32-stream": { 587 | "version": "2.0.0", 588 | "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-2.0.0.tgz", 589 | "integrity": "sha1-483TtN8xaN10494/u8t7KX/pCPQ=", 590 | "requires": { 591 | "crc": "3.5.0", 592 | "readable-stream": "2.3.6" 593 | } 594 | }, 595 | "create-error-class": { 596 | "version": "3.0.2", 597 | "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", 598 | "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", 599 | "requires": { 600 | "capture-stack-trace": "1.0.0" 601 | } 602 | }, 603 | "cross-spawn": { 604 | "version": "5.1.0", 605 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 606 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 607 | "requires": { 608 | "lru-cache": "4.1.3", 609 | "shebang-command": "1.2.0", 610 | "which": "1.3.1" 611 | } 612 | }, 613 | "crypto-random-string": { 614 | "version": "1.0.0", 615 | "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", 616 | "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" 617 | }, 618 | "debug": { 619 | "version": "2.6.9", 620 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 621 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 622 | "requires": { 623 | "ms": "2.0.0" 624 | } 625 | }, 626 | "decompress": { 627 | "version": "4.2.0", 628 | "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz", 629 | "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", 630 | "requires": { 631 | "decompress-tar": "4.1.1", 632 | "decompress-tarbz2": "4.1.1", 633 | "decompress-targz": "4.1.1", 634 | "decompress-unzip": "4.0.1", 635 | "graceful-fs": "4.1.11", 636 | "make-dir": "1.3.0", 637 | "pify": "2.3.0", 638 | "strip-dirs": "2.1.0" 639 | } 640 | }, 641 | "decompress-tar": { 642 | "version": "4.1.1", 643 | "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", 644 | "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", 645 | "requires": { 646 | "file-type": "5.2.0", 647 | "is-stream": "1.1.0", 648 | "tar-stream": "1.6.1" 649 | } 650 | }, 651 | "decompress-tarbz2": { 652 | "version": "4.1.1", 653 | "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", 654 | "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", 655 | "requires": { 656 | "decompress-tar": "4.1.1", 657 | "file-type": "6.2.0", 658 | "is-stream": "1.1.0", 659 | "seek-bzip": "1.0.5", 660 | "unbzip2-stream": "1.2.5" 661 | }, 662 | "dependencies": { 663 | "file-type": { 664 | "version": "6.2.0", 665 | "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", 666 | "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==" 667 | } 668 | } 669 | }, 670 | "decompress-targz": { 671 | "version": "4.1.1", 672 | "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", 673 | "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", 674 | "requires": { 675 | "decompress-tar": "4.1.1", 676 | "file-type": "5.2.0", 677 | "is-stream": "1.1.0" 678 | } 679 | }, 680 | "decompress-unzip": { 681 | "version": "4.0.1", 682 | "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", 683 | "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", 684 | "requires": { 685 | "file-type": "3.9.0", 686 | "get-stream": "2.3.1", 687 | "pify": "2.3.0", 688 | "yauzl": "2.10.0" 689 | }, 690 | "dependencies": { 691 | "file-type": { 692 | "version": "3.9.0", 693 | "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", 694 | "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" 695 | }, 696 | "get-stream": { 697 | "version": "2.3.1", 698 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", 699 | "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", 700 | "requires": { 701 | "object-assign": "4.1.1", 702 | "pinkie-promise": "2.0.1" 703 | } 704 | } 705 | } 706 | }, 707 | "deep-extend": { 708 | "version": "0.6.0", 709 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 710 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" 711 | }, 712 | "delayed-stream": { 713 | "version": "1.0.0", 714 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 715 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 716 | }, 717 | "delegates": { 718 | "version": "1.0.0", 719 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 720 | "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" 721 | }, 722 | "depd": { 723 | "version": "1.1.2", 724 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 725 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 726 | }, 727 | "destroy": { 728 | "version": "1.0.4", 729 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 730 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 731 | }, 732 | "dot-prop": { 733 | "version": "4.2.0", 734 | "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", 735 | "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", 736 | "requires": { 737 | "is-obj": "1.0.1" 738 | } 739 | }, 740 | "download": { 741 | "version": "5.0.3", 742 | "resolved": "https://registry.npmjs.org/download/-/download-5.0.3.tgz", 743 | "integrity": "sha1-Y1N/l3+ZJmow64oqL70fILgAD3o=", 744 | "requires": { 745 | "caw": "2.0.1", 746 | "decompress": "4.2.0", 747 | "filenamify": "2.1.0", 748 | "get-stream": "3.0.0", 749 | "got": "6.7.1", 750 | "mkdirp": "0.5.1", 751 | "pify": "2.3.0" 752 | } 753 | }, 754 | "duplexer3": { 755 | "version": "0.1.4", 756 | "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", 757 | "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" 758 | }, 759 | "ee-first": { 760 | "version": "1.1.1", 761 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 762 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 763 | }, 764 | "encodeurl": { 765 | "version": "1.0.2", 766 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 767 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 768 | }, 769 | "encoding": { 770 | "version": "0.1.12", 771 | "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", 772 | "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", 773 | "requires": { 774 | "iconv-lite": "0.4.23" 775 | } 776 | }, 777 | "end-of-stream": { 778 | "version": "1.4.1", 779 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", 780 | "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", 781 | "requires": { 782 | "once": "1.4.0" 783 | } 784 | }, 785 | "es6-promise": { 786 | "version": "4.2.4", 787 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", 788 | "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==" 789 | }, 790 | "es6-promisify": { 791 | "version": "5.0.0", 792 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", 793 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", 794 | "requires": { 795 | "es6-promise": "4.2.4" 796 | } 797 | }, 798 | "escape-html": { 799 | "version": "1.0.3", 800 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 801 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 802 | }, 803 | "escape-string-regexp": { 804 | "version": "1.0.5", 805 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 806 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 807 | }, 808 | "esprima": { 809 | "version": "4.0.0", 810 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", 811 | "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==" 812 | }, 813 | "etag": { 814 | "version": "1.8.1", 815 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 816 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 817 | }, 818 | "events": { 819 | "version": "1.1.1", 820 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 821 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" 822 | }, 823 | "execa": { 824 | "version": "0.7.0", 825 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", 826 | "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", 827 | "requires": { 828 | "cross-spawn": "5.1.0", 829 | "get-stream": "3.0.0", 830 | "is-stream": "1.1.0", 831 | "npm-run-path": "2.0.2", 832 | "p-finally": "1.0.0", 833 | "signal-exit": "3.0.2", 834 | "strip-eof": "1.0.0" 835 | } 836 | }, 837 | "exit-hook": { 838 | "version": "1.1.1", 839 | "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", 840 | "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=" 841 | }, 842 | "express": { 843 | "version": "4.16.3", 844 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", 845 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", 846 | "requires": { 847 | "accepts": "1.3.5", 848 | "array-flatten": "1.1.1", 849 | "body-parser": "1.18.2", 850 | "content-disposition": "0.5.2", 851 | "content-type": "1.0.4", 852 | "cookie": "0.3.1", 853 | "cookie-signature": "1.0.6", 854 | "debug": "2.6.9", 855 | "depd": "1.1.2", 856 | "encodeurl": "1.0.2", 857 | "escape-html": "1.0.3", 858 | "etag": "1.8.1", 859 | "finalhandler": "1.1.1", 860 | "fresh": "0.5.2", 861 | "merge-descriptors": "1.0.1", 862 | "methods": "1.1.2", 863 | "on-finished": "2.3.0", 864 | "parseurl": "1.3.2", 865 | "path-to-regexp": "0.1.7", 866 | "proxy-addr": "2.0.3", 867 | "qs": "6.5.1", 868 | "range-parser": "1.2.0", 869 | "safe-buffer": "5.1.1", 870 | "send": "0.16.2", 871 | "serve-static": "1.13.2", 872 | "setprototypeof": "1.1.0", 873 | "statuses": "1.4.0", 874 | "type-is": "1.6.16", 875 | "utils-merge": "1.0.1", 876 | "vary": "1.1.2" 877 | }, 878 | "dependencies": { 879 | "body-parser": { 880 | "version": "1.18.2", 881 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", 882 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 883 | "requires": { 884 | "bytes": "3.0.0", 885 | "content-type": "1.0.4", 886 | "debug": "2.6.9", 887 | "depd": "1.1.2", 888 | "http-errors": "1.6.3", 889 | "iconv-lite": "0.4.19", 890 | "on-finished": "2.3.0", 891 | "qs": "6.5.1", 892 | "raw-body": "2.3.2", 893 | "type-is": "1.6.16" 894 | } 895 | }, 896 | "iconv-lite": { 897 | "version": "0.4.19", 898 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 899 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 900 | }, 901 | "qs": { 902 | "version": "6.5.1", 903 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 904 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 905 | }, 906 | "raw-body": { 907 | "version": "2.3.2", 908 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 909 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 910 | "requires": { 911 | "bytes": "3.0.0", 912 | "http-errors": "1.6.2", 913 | "iconv-lite": "0.4.19", 914 | "unpipe": "1.0.0" 915 | }, 916 | "dependencies": { 917 | "depd": { 918 | "version": "1.1.1", 919 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 920 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 921 | }, 922 | "http-errors": { 923 | "version": "1.6.2", 924 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 925 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 926 | "requires": { 927 | "depd": "1.1.1", 928 | "inherits": "2.0.3", 929 | "setprototypeof": "1.0.3", 930 | "statuses": "1.4.0" 931 | } 932 | }, 933 | "setprototypeof": { 934 | "version": "1.0.3", 935 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 936 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 937 | } 938 | } 939 | }, 940 | "statuses": { 941 | "version": "1.4.0", 942 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 943 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 944 | } 945 | } 946 | }, 947 | "extend": { 948 | "version": "3.0.1", 949 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", 950 | "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" 951 | }, 952 | "external-editor": { 953 | "version": "1.1.1", 954 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-1.1.1.tgz", 955 | "integrity": "sha1-Etew24UPf/fnCBuvQAVwAGDEYAs=", 956 | "requires": { 957 | "extend": "3.0.1", 958 | "spawn-sync": "1.0.15", 959 | "tmp": "0.0.29" 960 | } 961 | }, 962 | "fast-levenshtein": { 963 | "version": "2.0.6", 964 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 965 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" 966 | }, 967 | "fd-slicer": { 968 | "version": "1.1.0", 969 | "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", 970 | "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", 971 | "requires": { 972 | "pend": "1.2.0" 973 | } 974 | }, 975 | "figures": { 976 | "version": "1.7.0", 977 | "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", 978 | "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", 979 | "requires": { 980 | "escape-string-regexp": "1.0.5", 981 | "object-assign": "4.1.1" 982 | } 983 | }, 984 | "file-type": { 985 | "version": "5.2.0", 986 | "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", 987 | "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=" 988 | }, 989 | "filename-reserved-regex": { 990 | "version": "2.0.0", 991 | "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", 992 | "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=" 993 | }, 994 | "filenamify": { 995 | "version": "2.1.0", 996 | "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz", 997 | "integrity": "sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==", 998 | "requires": { 999 | "filename-reserved-regex": "2.0.0", 1000 | "strip-outer": "1.0.1", 1001 | "trim-repeated": "1.0.0" 1002 | } 1003 | }, 1004 | "filesize": { 1005 | "version": "3.6.1", 1006 | "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", 1007 | "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==" 1008 | }, 1009 | "finalhandler": { 1010 | "version": "1.1.1", 1011 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 1012 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 1013 | "requires": { 1014 | "debug": "2.6.9", 1015 | "encodeurl": "1.0.2", 1016 | "escape-html": "1.0.3", 1017 | "on-finished": "2.3.0", 1018 | "parseurl": "1.3.2", 1019 | "statuses": "1.4.0", 1020 | "unpipe": "1.0.0" 1021 | }, 1022 | "dependencies": { 1023 | "statuses": { 1024 | "version": "1.4.0", 1025 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 1026 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 1027 | } 1028 | } 1029 | }, 1030 | "form-data": { 1031 | "version": "2.3.2", 1032 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", 1033 | "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", 1034 | "requires": { 1035 | "asynckit": "0.4.0", 1036 | "combined-stream": "1.0.6", 1037 | "mime-types": "2.1.18" 1038 | } 1039 | }, 1040 | "formidable": { 1041 | "version": "1.2.1", 1042 | "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", 1043 | "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" 1044 | }, 1045 | "forwarded": { 1046 | "version": "0.1.2", 1047 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 1048 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 1049 | }, 1050 | "fresh": { 1051 | "version": "0.5.2", 1052 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 1053 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 1054 | }, 1055 | "fs-constants": { 1056 | "version": "1.0.0", 1057 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 1058 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" 1059 | }, 1060 | "fs-extra": { 1061 | "version": "0.26.7", 1062 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", 1063 | "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", 1064 | "requires": { 1065 | "graceful-fs": "4.1.11", 1066 | "jsonfile": "2.4.0", 1067 | "klaw": "1.3.1", 1068 | "path-is-absolute": "1.0.1", 1069 | "rimraf": "2.6.2" 1070 | } 1071 | }, 1072 | "fs.realpath": { 1073 | "version": "1.0.0", 1074 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1075 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 1076 | }, 1077 | "gauge": { 1078 | "version": "1.2.7", 1079 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", 1080 | "integrity": "sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=", 1081 | "requires": { 1082 | "ansi": "0.3.1", 1083 | "has-unicode": "2.0.1", 1084 | "lodash.pad": "4.5.1", 1085 | "lodash.padend": "4.6.1", 1086 | "lodash.padstart": "4.6.1" 1087 | } 1088 | }, 1089 | "get-proxy": { 1090 | "version": "2.1.0", 1091 | "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", 1092 | "integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==", 1093 | "requires": { 1094 | "npm-conf": "1.1.3" 1095 | } 1096 | }, 1097 | "get-stdin": { 1098 | "version": "5.0.1", 1099 | "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", 1100 | "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=" 1101 | }, 1102 | "get-stream": { 1103 | "version": "3.0.0", 1104 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 1105 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" 1106 | }, 1107 | "glob": { 1108 | "version": "7.1.2", 1109 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 1110 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 1111 | "requires": { 1112 | "fs.realpath": "1.0.0", 1113 | "inflight": "1.0.6", 1114 | "inherits": "2.0.3", 1115 | "minimatch": "3.0.4", 1116 | "once": "1.4.0", 1117 | "path-is-absolute": "1.0.1" 1118 | } 1119 | }, 1120 | "global-dirs": { 1121 | "version": "0.1.1", 1122 | "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", 1123 | "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", 1124 | "requires": { 1125 | "ini": "1.3.5" 1126 | } 1127 | }, 1128 | "globby": { 1129 | "version": "6.1.0", 1130 | "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", 1131 | "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", 1132 | "requires": { 1133 | "array-union": "1.0.2", 1134 | "glob": "7.1.2", 1135 | "object-assign": "4.1.1", 1136 | "pify": "2.3.0", 1137 | "pinkie-promise": "2.0.1" 1138 | } 1139 | }, 1140 | "got": { 1141 | "version": "6.7.1", 1142 | "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", 1143 | "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", 1144 | "requires": { 1145 | "create-error-class": "3.0.2", 1146 | "duplexer3": "0.1.4", 1147 | "get-stream": "3.0.0", 1148 | "is-redirect": "1.0.0", 1149 | "is-retry-allowed": "1.1.0", 1150 | "is-stream": "1.1.0", 1151 | "lowercase-keys": "1.0.1", 1152 | "safe-buffer": "5.1.1", 1153 | "timed-out": "4.0.1", 1154 | "unzip-response": "2.0.1", 1155 | "url-parse-lax": "1.0.0" 1156 | } 1157 | }, 1158 | "graceful-fs": { 1159 | "version": "4.1.11", 1160 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 1161 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" 1162 | }, 1163 | "graceful-readlink": { 1164 | "version": "1.0.1", 1165 | "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", 1166 | "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" 1167 | }, 1168 | "graphlib": { 1169 | "version": "2.1.5", 1170 | "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.5.tgz", 1171 | "integrity": "sha512-XvtbqCcw+EM5SqQrIetIKKD+uZVNQtDPD1goIg7K73RuRZtVI5rYMdcCVSHm/AS1sCBZ7vt0p5WgXouucHQaOA==", 1172 | "requires": { 1173 | "lodash": "4.17.10" 1174 | } 1175 | }, 1176 | "has-ansi": { 1177 | "version": "2.0.0", 1178 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 1179 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 1180 | "requires": { 1181 | "ansi-regex": "2.1.1" 1182 | } 1183 | }, 1184 | "has-flag": { 1185 | "version": "3.0.0", 1186 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 1187 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 1188 | }, 1189 | "has-symbol-support-x": { 1190 | "version": "1.4.2", 1191 | "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", 1192 | "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" 1193 | }, 1194 | "has-to-string-tag-x": { 1195 | "version": "1.4.1", 1196 | "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", 1197 | "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", 1198 | "requires": { 1199 | "has-symbol-support-x": "1.4.2" 1200 | } 1201 | }, 1202 | "has-unicode": { 1203 | "version": "2.0.1", 1204 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 1205 | "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" 1206 | }, 1207 | "http-errors": { 1208 | "version": "1.6.3", 1209 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 1210 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 1211 | "requires": { 1212 | "depd": "1.1.2", 1213 | "inherits": "2.0.3", 1214 | "setprototypeof": "1.1.0", 1215 | "statuses": "1.5.0" 1216 | } 1217 | }, 1218 | "https-proxy-agent": { 1219 | "version": "2.2.1", 1220 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", 1221 | "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", 1222 | "requires": { 1223 | "agent-base": "4.2.1", 1224 | "debug": "3.1.0" 1225 | }, 1226 | "dependencies": { 1227 | "debug": { 1228 | "version": "3.1.0", 1229 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 1230 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 1231 | "requires": { 1232 | "ms": "2.0.0" 1233 | } 1234 | } 1235 | } 1236 | }, 1237 | "iconv-lite": { 1238 | "version": "0.4.23", 1239 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 1240 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 1241 | "requires": { 1242 | "safer-buffer": "2.1.2" 1243 | } 1244 | }, 1245 | "ieee754": { 1246 | "version": "1.1.8", 1247 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", 1248 | "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" 1249 | }, 1250 | "import-lazy": { 1251 | "version": "2.1.0", 1252 | "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", 1253 | "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" 1254 | }, 1255 | "imurmurhash": { 1256 | "version": "0.1.4", 1257 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1258 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" 1259 | }, 1260 | "inflight": { 1261 | "version": "1.0.6", 1262 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1263 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1264 | "requires": { 1265 | "once": "1.4.0", 1266 | "wrappy": "1.0.2" 1267 | } 1268 | }, 1269 | "inherits": { 1270 | "version": "2.0.3", 1271 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 1272 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 1273 | }, 1274 | "ini": { 1275 | "version": "1.3.5", 1276 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", 1277 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" 1278 | }, 1279 | "inquirer": { 1280 | "version": "1.2.3", 1281 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-1.2.3.tgz", 1282 | "integrity": "sha1-TexvMvN+97sLLtPx0aXD9UUHSRg=", 1283 | "requires": { 1284 | "ansi-escapes": "1.4.0", 1285 | "chalk": "1.1.3", 1286 | "cli-cursor": "1.0.2", 1287 | "cli-width": "2.2.0", 1288 | "external-editor": "1.1.1", 1289 | "figures": "1.7.0", 1290 | "lodash": "4.17.10", 1291 | "mute-stream": "0.0.6", 1292 | "pinkie-promise": "2.0.1", 1293 | "run-async": "2.3.0", 1294 | "rx": "4.1.0", 1295 | "string-width": "1.0.2", 1296 | "strip-ansi": "3.0.1", 1297 | "through": "2.3.8" 1298 | }, 1299 | "dependencies": { 1300 | "ansi-styles": { 1301 | "version": "2.2.1", 1302 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 1303 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" 1304 | }, 1305 | "chalk": { 1306 | "version": "1.1.3", 1307 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 1308 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 1309 | "requires": { 1310 | "ansi-styles": "2.2.1", 1311 | "escape-string-regexp": "1.0.5", 1312 | "has-ansi": "2.0.0", 1313 | "strip-ansi": "3.0.1", 1314 | "supports-color": "2.0.0" 1315 | } 1316 | }, 1317 | "supports-color": { 1318 | "version": "2.0.0", 1319 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 1320 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" 1321 | } 1322 | } 1323 | }, 1324 | "ipaddr.js": { 1325 | "version": "1.6.0", 1326 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", 1327 | "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" 1328 | }, 1329 | "is-ci": { 1330 | "version": "1.1.0", 1331 | "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz", 1332 | "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==", 1333 | "requires": { 1334 | "ci-info": "1.1.3" 1335 | } 1336 | }, 1337 | "is-docker": { 1338 | "version": "1.1.0", 1339 | "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-1.1.0.tgz", 1340 | "integrity": "sha1-8EN01O7lMQ6ajhE78UlUEeRhdqE=" 1341 | }, 1342 | "is-fullwidth-code-point": { 1343 | "version": "1.0.0", 1344 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 1345 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 1346 | "requires": { 1347 | "number-is-nan": "1.0.1" 1348 | } 1349 | }, 1350 | "is-installed-globally": { 1351 | "version": "0.1.0", 1352 | "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", 1353 | "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", 1354 | "requires": { 1355 | "global-dirs": "0.1.1", 1356 | "is-path-inside": "1.0.1" 1357 | } 1358 | }, 1359 | "is-natural-number": { 1360 | "version": "4.0.1", 1361 | "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", 1362 | "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=" 1363 | }, 1364 | "is-npm": { 1365 | "version": "1.0.0", 1366 | "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", 1367 | "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" 1368 | }, 1369 | "is-obj": { 1370 | "version": "1.0.1", 1371 | "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", 1372 | "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" 1373 | }, 1374 | "is-object": { 1375 | "version": "1.0.1", 1376 | "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", 1377 | "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" 1378 | }, 1379 | "is-path-inside": { 1380 | "version": "1.0.1", 1381 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", 1382 | "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", 1383 | "requires": { 1384 | "path-is-inside": "1.0.2" 1385 | } 1386 | }, 1387 | "is-promise": { 1388 | "version": "2.1.0", 1389 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", 1390 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" 1391 | }, 1392 | "is-redirect": { 1393 | "version": "1.0.0", 1394 | "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", 1395 | "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" 1396 | }, 1397 | "is-retry-allowed": { 1398 | "version": "1.1.0", 1399 | "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", 1400 | "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" 1401 | }, 1402 | "is-stream": { 1403 | "version": "1.1.0", 1404 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 1405 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 1406 | }, 1407 | "is-wsl": { 1408 | "version": "1.1.0", 1409 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", 1410 | "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" 1411 | }, 1412 | "isarray": { 1413 | "version": "1.0.0", 1414 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1415 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 1416 | }, 1417 | "isexe": { 1418 | "version": "2.0.0", 1419 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1420 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 1421 | }, 1422 | "isomorphic-fetch": { 1423 | "version": "2.2.1", 1424 | "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", 1425 | "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", 1426 | "requires": { 1427 | "node-fetch": "1.7.3", 1428 | "whatwg-fetch": "2.0.4" 1429 | } 1430 | }, 1431 | "isurl": { 1432 | "version": "1.0.0", 1433 | "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", 1434 | "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", 1435 | "requires": { 1436 | "has-to-string-tag-x": "1.4.1", 1437 | "is-object": "1.0.1" 1438 | } 1439 | }, 1440 | "jmespath": { 1441 | "version": "0.15.0", 1442 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", 1443 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" 1444 | }, 1445 | "js-yaml": { 1446 | "version": "3.12.0", 1447 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", 1448 | "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", 1449 | "requires": { 1450 | "argparse": "1.0.10", 1451 | "esprima": "4.0.0" 1452 | } 1453 | }, 1454 | "json-cycle": { 1455 | "version": "1.3.0", 1456 | "resolved": "https://registry.npmjs.org/json-cycle/-/json-cycle-1.3.0.tgz", 1457 | "integrity": "sha512-FD/SedD78LCdSvJaOUQAXseT8oQBb5z6IVYaQaCrVUlu9zOAr1BDdKyVYQaSD/GDsAMrXpKcOyBD4LIl8nfjHw==" 1458 | }, 1459 | "json-refs": { 1460 | "version": "2.1.7", 1461 | "resolved": "https://registry.npmjs.org/json-refs/-/json-refs-2.1.7.tgz", 1462 | "integrity": "sha1-uesB/in16j6Sh48VrqEK04taz4k=", 1463 | "requires": { 1464 | "commander": "2.16.0", 1465 | "graphlib": "2.1.5", 1466 | "js-yaml": "3.12.0", 1467 | "native-promise-only": "0.8.1", 1468 | "path-loader": "1.0.4", 1469 | "slash": "1.0.0", 1470 | "uri-js": "3.0.2" 1471 | }, 1472 | "dependencies": { 1473 | "commander": { 1474 | "version": "2.16.0", 1475 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz", 1476 | "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==" 1477 | } 1478 | } 1479 | }, 1480 | "json-stringify-safe": { 1481 | "version": "5.0.1", 1482 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 1483 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 1484 | }, 1485 | "jsonfile": { 1486 | "version": "2.4.0", 1487 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", 1488 | "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", 1489 | "requires": { 1490 | "graceful-fs": "4.1.11" 1491 | } 1492 | }, 1493 | "jwt-decode": { 1494 | "version": "2.2.0", 1495 | "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", 1496 | "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=" 1497 | }, 1498 | "klaw": { 1499 | "version": "1.3.1", 1500 | "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", 1501 | "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", 1502 | "requires": { 1503 | "graceful-fs": "4.1.11" 1504 | } 1505 | }, 1506 | "latest-version": { 1507 | "version": "3.1.0", 1508 | "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", 1509 | "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", 1510 | "requires": { 1511 | "package-json": "4.0.1" 1512 | } 1513 | }, 1514 | "lazystream": { 1515 | "version": "1.0.0", 1516 | "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", 1517 | "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", 1518 | "requires": { 1519 | "readable-stream": "2.3.6" 1520 | } 1521 | }, 1522 | "lodash": { 1523 | "version": "4.17.10", 1524 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", 1525 | "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" 1526 | }, 1527 | "lodash.difference": { 1528 | "version": "4.5.0", 1529 | "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", 1530 | "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" 1531 | }, 1532 | "lodash.pad": { 1533 | "version": "4.5.1", 1534 | "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", 1535 | "integrity": "sha1-QzCUmoM6fI2iLMIPaibE1Z3runA=" 1536 | }, 1537 | "lodash.padend": { 1538 | "version": "4.6.1", 1539 | "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", 1540 | "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=" 1541 | }, 1542 | "lodash.padstart": { 1543 | "version": "4.6.1", 1544 | "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", 1545 | "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=" 1546 | }, 1547 | "lodash.uniq": { 1548 | "version": "4.5.0", 1549 | "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", 1550 | "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" 1551 | }, 1552 | "lowercase-keys": { 1553 | "version": "1.0.1", 1554 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", 1555 | "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" 1556 | }, 1557 | "lru-cache": { 1558 | "version": "4.1.3", 1559 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", 1560 | "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", 1561 | "requires": { 1562 | "pseudomap": "1.0.2", 1563 | "yallist": "2.1.2" 1564 | } 1565 | }, 1566 | "lsmod": { 1567 | "version": "1.0.0", 1568 | "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", 1569 | "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=" 1570 | }, 1571 | "make-dir": { 1572 | "version": "1.3.0", 1573 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", 1574 | "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", 1575 | "requires": { 1576 | "pify": "3.0.0" 1577 | }, 1578 | "dependencies": { 1579 | "pify": { 1580 | "version": "3.0.0", 1581 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 1582 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" 1583 | } 1584 | } 1585 | }, 1586 | "media-typer": { 1587 | "version": "0.3.0", 1588 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1589 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 1590 | }, 1591 | "merge-descriptors": { 1592 | "version": "1.0.1", 1593 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1594 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 1595 | }, 1596 | "methods": { 1597 | "version": "1.1.2", 1598 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1599 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 1600 | }, 1601 | "mime": { 1602 | "version": "1.4.1", 1603 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 1604 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 1605 | }, 1606 | "mime-db": { 1607 | "version": "1.33.0", 1608 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 1609 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" 1610 | }, 1611 | "mime-types": { 1612 | "version": "2.1.18", 1613 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 1614 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 1615 | "requires": { 1616 | "mime-db": "1.33.0" 1617 | } 1618 | }, 1619 | "minimatch": { 1620 | "version": "3.0.4", 1621 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1622 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1623 | "requires": { 1624 | "brace-expansion": "1.1.11" 1625 | } 1626 | }, 1627 | "minimist": { 1628 | "version": "1.2.0", 1629 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 1630 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 1631 | }, 1632 | "mkdirp": { 1633 | "version": "0.5.1", 1634 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1635 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 1636 | "requires": { 1637 | "minimist": "0.0.8" 1638 | }, 1639 | "dependencies": { 1640 | "minimist": { 1641 | "version": "0.0.8", 1642 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1643 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 1644 | } 1645 | } 1646 | }, 1647 | "moment": { 1648 | "version": "2.22.2", 1649 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", 1650 | "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" 1651 | }, 1652 | "ms": { 1653 | "version": "2.0.0", 1654 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1655 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1656 | }, 1657 | "mute-stream": { 1658 | "version": "0.0.6", 1659 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.6.tgz", 1660 | "integrity": "sha1-SJYrGeFp/R38JAs/HnMXYnu8R9s=" 1661 | }, 1662 | "native-promise-only": { 1663 | "version": "0.8.1", 1664 | "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", 1665 | "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=" 1666 | }, 1667 | "negotiator": { 1668 | "version": "0.6.1", 1669 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 1670 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 1671 | }, 1672 | "node-fetch": { 1673 | "version": "1.7.3", 1674 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", 1675 | "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", 1676 | "requires": { 1677 | "encoding": "0.1.12", 1678 | "is-stream": "1.1.0" 1679 | } 1680 | }, 1681 | "normalize-path": { 1682 | "version": "2.1.1", 1683 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", 1684 | "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", 1685 | "requires": { 1686 | "remove-trailing-separator": "1.1.0" 1687 | } 1688 | }, 1689 | "npm-conf": { 1690 | "version": "1.1.3", 1691 | "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", 1692 | "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", 1693 | "requires": { 1694 | "config-chain": "1.1.11", 1695 | "pify": "3.0.0" 1696 | }, 1697 | "dependencies": { 1698 | "pify": { 1699 | "version": "3.0.0", 1700 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 1701 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" 1702 | } 1703 | } 1704 | }, 1705 | "npm-run-path": { 1706 | "version": "2.0.2", 1707 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 1708 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 1709 | "requires": { 1710 | "path-key": "2.0.1" 1711 | } 1712 | }, 1713 | "npmlog": { 1714 | "version": "2.0.4", 1715 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-2.0.4.tgz", 1716 | "integrity": "sha1-mLUlMPJRTKkNCexbIsiEZyI3VpI=", 1717 | "requires": { 1718 | "ansi": "0.3.1", 1719 | "are-we-there-yet": "1.1.5", 1720 | "gauge": "1.2.7" 1721 | } 1722 | }, 1723 | "number-is-nan": { 1724 | "version": "1.0.1", 1725 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 1726 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 1727 | }, 1728 | "object-assign": { 1729 | "version": "4.1.1", 1730 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1731 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 1732 | }, 1733 | "object-hash": { 1734 | "version": "1.3.0", 1735 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.0.tgz", 1736 | "integrity": "sha512-05KzQ70lSeGSrZJQXE5wNDiTkBJDlUT/myi6RX9dVIvz7a7Qh4oH93BQdiPMn27nldYvVQCKMUaM83AfizZlsQ==" 1737 | }, 1738 | "on-finished": { 1739 | "version": "2.3.0", 1740 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 1741 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1742 | "requires": { 1743 | "ee-first": "1.1.1" 1744 | } 1745 | }, 1746 | "once": { 1747 | "version": "1.4.0", 1748 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1749 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1750 | "requires": { 1751 | "wrappy": "1.0.2" 1752 | } 1753 | }, 1754 | "onetime": { 1755 | "version": "1.1.0", 1756 | "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", 1757 | "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" 1758 | }, 1759 | "opn": { 1760 | "version": "5.3.0", 1761 | "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", 1762 | "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", 1763 | "requires": { 1764 | "is-wsl": "1.1.0" 1765 | } 1766 | }, 1767 | "os-shim": { 1768 | "version": "0.1.3", 1769 | "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", 1770 | "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=" 1771 | }, 1772 | "os-tmpdir": { 1773 | "version": "1.0.2", 1774 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 1775 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 1776 | }, 1777 | "p-finally": { 1778 | "version": "1.0.0", 1779 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 1780 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" 1781 | }, 1782 | "package-json": { 1783 | "version": "4.0.1", 1784 | "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", 1785 | "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", 1786 | "requires": { 1787 | "got": "6.7.1", 1788 | "registry-auth-token": "3.3.2", 1789 | "registry-url": "3.1.0", 1790 | "semver": "5.5.0" 1791 | } 1792 | }, 1793 | "parseurl": { 1794 | "version": "1.3.2", 1795 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 1796 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 1797 | }, 1798 | "path-is-absolute": { 1799 | "version": "1.0.1", 1800 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1801 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1802 | }, 1803 | "path-is-inside": { 1804 | "version": "1.0.2", 1805 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 1806 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" 1807 | }, 1808 | "path-key": { 1809 | "version": "2.0.1", 1810 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 1811 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" 1812 | }, 1813 | "path-loader": { 1814 | "version": "1.0.4", 1815 | "resolved": "https://registry.npmjs.org/path-loader/-/path-loader-1.0.4.tgz", 1816 | "integrity": "sha512-k/IPo9OWyofATP5gwIehHHQoFShS37zsSIsejKe6fjI+tqK+FnRpiSg4ZfWUpxb0g2PfCreWPqBD4ayjqjqkdQ==", 1817 | "requires": { 1818 | "native-promise-only": "0.8.1", 1819 | "superagent": "3.8.3" 1820 | } 1821 | }, 1822 | "path-to-regexp": { 1823 | "version": "0.1.7", 1824 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1825 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 1826 | }, 1827 | "pend": { 1828 | "version": "1.2.0", 1829 | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", 1830 | "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" 1831 | }, 1832 | "pify": { 1833 | "version": "2.3.0", 1834 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1835 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" 1836 | }, 1837 | "pinkie": { 1838 | "version": "2.0.4", 1839 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 1840 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" 1841 | }, 1842 | "pinkie-promise": { 1843 | "version": "2.0.1", 1844 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 1845 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 1846 | "requires": { 1847 | "pinkie": "2.0.4" 1848 | } 1849 | }, 1850 | "prepend-http": { 1851 | "version": "1.0.4", 1852 | "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", 1853 | "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" 1854 | }, 1855 | "process-nextick-args": { 1856 | "version": "2.0.0", 1857 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 1858 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" 1859 | }, 1860 | "promise-queue": { 1861 | "version": "2.2.5", 1862 | "resolved": "https://registry.npmjs.org/promise-queue/-/promise-queue-2.2.5.tgz", 1863 | "integrity": "sha1-L29ffA9tCBCelnZZx5uIqe1ek7Q=" 1864 | }, 1865 | "proto-list": { 1866 | "version": "1.2.4", 1867 | "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", 1868 | "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" 1869 | }, 1870 | "proxy-addr": { 1871 | "version": "2.0.3", 1872 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", 1873 | "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", 1874 | "requires": { 1875 | "forwarded": "0.1.2", 1876 | "ipaddr.js": "1.6.0" 1877 | } 1878 | }, 1879 | "pseudomap": { 1880 | "version": "1.0.2", 1881 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 1882 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 1883 | }, 1884 | "punycode": { 1885 | "version": "1.3.2", 1886 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 1887 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 1888 | }, 1889 | "qs": { 1890 | "version": "6.5.2", 1891 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 1892 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 1893 | }, 1894 | "querystring": { 1895 | "version": "0.2.0", 1896 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 1897 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 1898 | }, 1899 | "querystringify": { 1900 | "version": "2.0.0", 1901 | "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz", 1902 | "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==" 1903 | }, 1904 | "ramda": { 1905 | "version": "0.24.1", 1906 | "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", 1907 | "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=" 1908 | }, 1909 | "range-parser": { 1910 | "version": "1.2.0", 1911 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 1912 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 1913 | }, 1914 | "raven": { 1915 | "version": "1.2.1", 1916 | "resolved": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz", 1917 | "integrity": "sha1-lJwTTbAooZC3u/j3kKrlQbfAIL0=", 1918 | "requires": { 1919 | "cookie": "0.3.1", 1920 | "json-stringify-safe": "5.0.1", 1921 | "lsmod": "1.0.0", 1922 | "stack-trace": "0.0.9", 1923 | "uuid": "3.0.0" 1924 | }, 1925 | "dependencies": { 1926 | "uuid": { 1927 | "version": "3.0.0", 1928 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", 1929 | "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" 1930 | } 1931 | } 1932 | }, 1933 | "raw-body": { 1934 | "version": "2.3.3", 1935 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 1936 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 1937 | "requires": { 1938 | "bytes": "3.0.0", 1939 | "http-errors": "1.6.3", 1940 | "iconv-lite": "0.4.23", 1941 | "unpipe": "1.0.0" 1942 | } 1943 | }, 1944 | "rc": { 1945 | "version": "1.2.8", 1946 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 1947 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 1948 | "requires": { 1949 | "deep-extend": "0.6.0", 1950 | "ini": "1.3.5", 1951 | "minimist": "1.2.0", 1952 | "strip-json-comments": "2.0.1" 1953 | } 1954 | }, 1955 | "readable-stream": { 1956 | "version": "2.3.6", 1957 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 1958 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 1959 | "requires": { 1960 | "core-util-is": "1.0.2", 1961 | "inherits": "2.0.3", 1962 | "isarray": "1.0.0", 1963 | "process-nextick-args": "2.0.0", 1964 | "safe-buffer": "5.1.1", 1965 | "string_decoder": "1.1.1", 1966 | "util-deprecate": "1.0.2" 1967 | } 1968 | }, 1969 | "regenerator-runtime": { 1970 | "version": "0.10.5", 1971 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", 1972 | "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" 1973 | }, 1974 | "registry-auth-token": { 1975 | "version": "3.3.2", 1976 | "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", 1977 | "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", 1978 | "requires": { 1979 | "rc": "1.2.8", 1980 | "safe-buffer": "5.1.1" 1981 | } 1982 | }, 1983 | "registry-url": { 1984 | "version": "3.1.0", 1985 | "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", 1986 | "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", 1987 | "requires": { 1988 | "rc": "1.2.8" 1989 | } 1990 | }, 1991 | "remove-trailing-separator": { 1992 | "version": "1.1.0", 1993 | "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", 1994 | "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" 1995 | }, 1996 | "replaceall": { 1997 | "version": "0.1.6", 1998 | "resolved": "https://registry.npmjs.org/replaceall/-/replaceall-0.1.6.tgz", 1999 | "integrity": "sha1-gdgax663LX9cSUKt8ml6MiBojY4=" 2000 | }, 2001 | "requires-port": { 2002 | "version": "1.0.0", 2003 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 2004 | "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" 2005 | }, 2006 | "restore-cursor": { 2007 | "version": "1.0.1", 2008 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", 2009 | "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", 2010 | "requires": { 2011 | "exit-hook": "1.1.1", 2012 | "onetime": "1.1.0" 2013 | } 2014 | }, 2015 | "rimraf": { 2016 | "version": "2.6.2", 2017 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 2018 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 2019 | "requires": { 2020 | "glob": "7.1.2" 2021 | } 2022 | }, 2023 | "run-async": { 2024 | "version": "2.3.0", 2025 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", 2026 | "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", 2027 | "requires": { 2028 | "is-promise": "2.1.0" 2029 | } 2030 | }, 2031 | "rx": { 2032 | "version": "4.1.0", 2033 | "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", 2034 | "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=" 2035 | }, 2036 | "safe-buffer": { 2037 | "version": "5.1.1", 2038 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 2039 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 2040 | }, 2041 | "safer-buffer": { 2042 | "version": "2.1.2", 2043 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 2044 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 2045 | }, 2046 | "sax": { 2047 | "version": "1.2.1", 2048 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 2049 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" 2050 | }, 2051 | "seek-bzip": { 2052 | "version": "1.0.5", 2053 | "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", 2054 | "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", 2055 | "requires": { 2056 | "commander": "2.8.1" 2057 | } 2058 | }, 2059 | "semver": { 2060 | "version": "5.5.0", 2061 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", 2062 | "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" 2063 | }, 2064 | "semver-diff": { 2065 | "version": "2.1.0", 2066 | "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", 2067 | "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", 2068 | "requires": { 2069 | "semver": "5.5.0" 2070 | } 2071 | }, 2072 | "semver-regex": { 2073 | "version": "1.0.0", 2074 | "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-1.0.0.tgz", 2075 | "integrity": "sha1-kqSWkGX5xwxpR1PVUkj8aPj2Usk=" 2076 | }, 2077 | "send": { 2078 | "version": "0.16.2", 2079 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 2080 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 2081 | "requires": { 2082 | "debug": "2.6.9", 2083 | "depd": "1.1.2", 2084 | "destroy": "1.0.4", 2085 | "encodeurl": "1.0.2", 2086 | "escape-html": "1.0.3", 2087 | "etag": "1.8.1", 2088 | "fresh": "0.5.2", 2089 | "http-errors": "1.6.3", 2090 | "mime": "1.4.1", 2091 | "ms": "2.0.0", 2092 | "on-finished": "2.3.0", 2093 | "range-parser": "1.2.0", 2094 | "statuses": "1.4.0" 2095 | }, 2096 | "dependencies": { 2097 | "statuses": { 2098 | "version": "1.4.0", 2099 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 2100 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 2101 | } 2102 | } 2103 | }, 2104 | "serve-static": { 2105 | "version": "1.13.2", 2106 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 2107 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 2108 | "requires": { 2109 | "encodeurl": "1.0.2", 2110 | "escape-html": "1.0.3", 2111 | "parseurl": "1.3.2", 2112 | "send": "0.16.2" 2113 | } 2114 | }, 2115 | "serverless": { 2116 | "version": "1.28.0", 2117 | "resolved": "https://registry.npmjs.org/serverless/-/serverless-1.28.0.tgz", 2118 | "integrity": "sha1-h5OvoK39RbA5a/Ey4w6/gL8efuY=", 2119 | "requires": { 2120 | "@serverless/fdk": "0.5.1", 2121 | "@serverless/platform-sdk": "0.1.5", 2122 | "archiver": "1.3.0", 2123 | "async": "1.5.2", 2124 | "aws-sdk": "2.270.1", 2125 | "bluebird": "3.5.1", 2126 | "chalk": "2.4.1", 2127 | "ci-info": "1.1.3", 2128 | "download": "5.0.3", 2129 | "fast-levenshtein": "2.0.6", 2130 | "filesize": "3.6.1", 2131 | "fs-extra": "0.26.7", 2132 | "get-stdin": "5.0.1", 2133 | "globby": "6.1.0", 2134 | "graceful-fs": "4.1.11", 2135 | "https-proxy-agent": "2.2.1", 2136 | "is-docker": "1.1.0", 2137 | "js-yaml": "3.12.0", 2138 | "json-cycle": "1.3.0", 2139 | "json-refs": "2.1.7", 2140 | "jwt-decode": "2.2.0", 2141 | "lodash": "4.17.10", 2142 | "minimist": "1.2.0", 2143 | "moment": "2.22.2", 2144 | "node-fetch": "1.7.3", 2145 | "object-hash": "1.3.0", 2146 | "promise-queue": "2.2.5", 2147 | "raven": "1.2.1", 2148 | "rc": "1.2.8", 2149 | "replaceall": "0.1.6", 2150 | "semver": "5.5.0", 2151 | "semver-regex": "1.0.0", 2152 | "tabtab": "2.2.2", 2153 | "update-notifier": "2.5.0", 2154 | "uuid": "2.0.3", 2155 | "write-file-atomic": "2.3.0", 2156 | "yaml-ast-parser": "0.0.34" 2157 | } 2158 | }, 2159 | "setprototypeof": { 2160 | "version": "1.1.0", 2161 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 2162 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 2163 | }, 2164 | "shebang-command": { 2165 | "version": "1.2.0", 2166 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 2167 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 2168 | "requires": { 2169 | "shebang-regex": "1.0.0" 2170 | } 2171 | }, 2172 | "shebang-regex": { 2173 | "version": "1.0.0", 2174 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 2175 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" 2176 | }, 2177 | "signal-exit": { 2178 | "version": "3.0.2", 2179 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 2180 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 2181 | }, 2182 | "slash": { 2183 | "version": "1.0.0", 2184 | "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", 2185 | "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" 2186 | }, 2187 | "source-map": { 2188 | "version": "0.6.1", 2189 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 2190 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 2191 | }, 2192 | "source-map-support": { 2193 | "version": "0.5.6", 2194 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz", 2195 | "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", 2196 | "requires": { 2197 | "buffer-from": "1.1.0", 2198 | "source-map": "0.6.1" 2199 | } 2200 | }, 2201 | "spawn-sync": { 2202 | "version": "1.0.15", 2203 | "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", 2204 | "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", 2205 | "requires": { 2206 | "concat-stream": "1.6.2", 2207 | "os-shim": "0.1.3" 2208 | } 2209 | }, 2210 | "sprintf-js": { 2211 | "version": "1.0.3", 2212 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 2213 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 2214 | }, 2215 | "stack-trace": { 2216 | "version": "0.0.9", 2217 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", 2218 | "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=" 2219 | }, 2220 | "statuses": { 2221 | "version": "1.5.0", 2222 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 2223 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 2224 | }, 2225 | "string-width": { 2226 | "version": "1.0.2", 2227 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 2228 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 2229 | "requires": { 2230 | "code-point-at": "1.1.0", 2231 | "is-fullwidth-code-point": "1.0.0", 2232 | "strip-ansi": "3.0.1" 2233 | } 2234 | }, 2235 | "string_decoder": { 2236 | "version": "1.1.1", 2237 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 2238 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 2239 | "requires": { 2240 | "safe-buffer": "5.1.1" 2241 | } 2242 | }, 2243 | "strip-ansi": { 2244 | "version": "3.0.1", 2245 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 2246 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 2247 | "requires": { 2248 | "ansi-regex": "2.1.1" 2249 | } 2250 | }, 2251 | "strip-dirs": { 2252 | "version": "2.1.0", 2253 | "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", 2254 | "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", 2255 | "requires": { 2256 | "is-natural-number": "4.0.1" 2257 | } 2258 | }, 2259 | "strip-eof": { 2260 | "version": "1.0.0", 2261 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 2262 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" 2263 | }, 2264 | "strip-json-comments": { 2265 | "version": "2.0.1", 2266 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 2267 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 2268 | }, 2269 | "strip-outer": { 2270 | "version": "1.0.1", 2271 | "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", 2272 | "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", 2273 | "requires": { 2274 | "escape-string-regexp": "1.0.5" 2275 | } 2276 | }, 2277 | "superagent": { 2278 | "version": "3.8.3", 2279 | "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", 2280 | "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", 2281 | "requires": { 2282 | "component-emitter": "1.2.1", 2283 | "cookiejar": "2.1.2", 2284 | "debug": "3.1.0", 2285 | "extend": "3.0.1", 2286 | "form-data": "2.3.2", 2287 | "formidable": "1.2.1", 2288 | "methods": "1.1.2", 2289 | "mime": "1.4.1", 2290 | "qs": "6.5.2", 2291 | "readable-stream": "2.3.6" 2292 | }, 2293 | "dependencies": { 2294 | "debug": { 2295 | "version": "3.1.0", 2296 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 2297 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 2298 | "requires": { 2299 | "ms": "2.0.0" 2300 | } 2301 | } 2302 | } 2303 | }, 2304 | "supports-color": { 2305 | "version": "5.4.0", 2306 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 2307 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 2308 | "requires": { 2309 | "has-flag": "3.0.0" 2310 | } 2311 | }, 2312 | "tabtab": { 2313 | "version": "2.2.2", 2314 | "resolved": "https://registry.npmjs.org/tabtab/-/tabtab-2.2.2.tgz", 2315 | "integrity": "sha1-egR/FDsBC0y9MfhX6ClhUSy/ThQ=", 2316 | "requires": { 2317 | "debug": "2.6.9", 2318 | "inquirer": "1.2.3", 2319 | "lodash.difference": "4.5.0", 2320 | "lodash.uniq": "4.5.0", 2321 | "minimist": "1.2.0", 2322 | "mkdirp": "0.5.1", 2323 | "npmlog": "2.0.4", 2324 | "object-assign": "4.1.1" 2325 | } 2326 | }, 2327 | "tar-stream": { 2328 | "version": "1.6.1", 2329 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", 2330 | "integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==", 2331 | "requires": { 2332 | "bl": "1.2.2", 2333 | "buffer-alloc": "1.2.0", 2334 | "end-of-stream": "1.4.1", 2335 | "fs-constants": "1.0.0", 2336 | "readable-stream": "2.3.6", 2337 | "to-buffer": "1.1.1", 2338 | "xtend": "4.0.1" 2339 | } 2340 | }, 2341 | "term-size": { 2342 | "version": "1.2.0", 2343 | "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", 2344 | "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", 2345 | "requires": { 2346 | "execa": "0.7.0" 2347 | } 2348 | }, 2349 | "through": { 2350 | "version": "2.3.8", 2351 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 2352 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 2353 | }, 2354 | "timed-out": { 2355 | "version": "4.0.1", 2356 | "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", 2357 | "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" 2358 | }, 2359 | "tmp": { 2360 | "version": "0.0.29", 2361 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.29.tgz", 2362 | "integrity": "sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA=", 2363 | "requires": { 2364 | "os-tmpdir": "1.0.2" 2365 | } 2366 | }, 2367 | "to-buffer": { 2368 | "version": "1.1.1", 2369 | "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", 2370 | "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" 2371 | }, 2372 | "trim-repeated": { 2373 | "version": "1.0.0", 2374 | "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", 2375 | "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", 2376 | "requires": { 2377 | "escape-string-regexp": "1.0.5" 2378 | } 2379 | }, 2380 | "tunnel-agent": { 2381 | "version": "0.6.0", 2382 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 2383 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 2384 | "requires": { 2385 | "safe-buffer": "5.1.1" 2386 | } 2387 | }, 2388 | "type-is": { 2389 | "version": "1.6.16", 2390 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 2391 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 2392 | "requires": { 2393 | "media-typer": "0.3.0", 2394 | "mime-types": "2.1.18" 2395 | } 2396 | }, 2397 | "typedarray": { 2398 | "version": "0.0.6", 2399 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 2400 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" 2401 | }, 2402 | "unbzip2-stream": { 2403 | "version": "1.2.5", 2404 | "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz", 2405 | "integrity": "sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==", 2406 | "requires": { 2407 | "buffer": "3.6.0", 2408 | "through": "2.3.8" 2409 | }, 2410 | "dependencies": { 2411 | "base64-js": { 2412 | "version": "0.0.8", 2413 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", 2414 | "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=" 2415 | }, 2416 | "buffer": { 2417 | "version": "3.6.0", 2418 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz", 2419 | "integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=", 2420 | "requires": { 2421 | "base64-js": "0.0.8", 2422 | "ieee754": "1.1.8", 2423 | "isarray": "1.0.0" 2424 | } 2425 | } 2426 | } 2427 | }, 2428 | "unique-string": { 2429 | "version": "1.0.0", 2430 | "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", 2431 | "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", 2432 | "requires": { 2433 | "crypto-random-string": "1.0.0" 2434 | } 2435 | }, 2436 | "unpipe": { 2437 | "version": "1.0.0", 2438 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 2439 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 2440 | }, 2441 | "unzip-response": { 2442 | "version": "2.0.1", 2443 | "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", 2444 | "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" 2445 | }, 2446 | "update-notifier": { 2447 | "version": "2.5.0", 2448 | "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", 2449 | "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", 2450 | "requires": { 2451 | "boxen": "1.3.0", 2452 | "chalk": "2.4.1", 2453 | "configstore": "3.1.2", 2454 | "import-lazy": "2.1.0", 2455 | "is-ci": "1.1.0", 2456 | "is-installed-globally": "0.1.0", 2457 | "is-npm": "1.0.0", 2458 | "latest-version": "3.1.0", 2459 | "semver-diff": "2.1.0", 2460 | "xdg-basedir": "3.0.0" 2461 | } 2462 | }, 2463 | "uri-js": { 2464 | "version": "3.0.2", 2465 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", 2466 | "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", 2467 | "requires": { 2468 | "punycode": "2.1.1" 2469 | }, 2470 | "dependencies": { 2471 | "punycode": { 2472 | "version": "2.1.1", 2473 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 2474 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 2475 | } 2476 | } 2477 | }, 2478 | "url": { 2479 | "version": "0.10.3", 2480 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 2481 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", 2482 | "requires": { 2483 | "punycode": "1.3.2", 2484 | "querystring": "0.2.0" 2485 | } 2486 | }, 2487 | "url-parse": { 2488 | "version": "1.4.1", 2489 | "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.1.tgz", 2490 | "integrity": "sha512-x95Td74QcvICAA0+qERaVkRpTGKyBHHYdwL2LXZm5t/gBtCB9KQSO/0zQgSTYEV1p0WcvSg79TLNPSvd5IDJMQ==", 2491 | "requires": { 2492 | "querystringify": "2.0.0", 2493 | "requires-port": "1.0.0" 2494 | } 2495 | }, 2496 | "url-parse-lax": { 2497 | "version": "1.0.0", 2498 | "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", 2499 | "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", 2500 | "requires": { 2501 | "prepend-http": "1.0.4" 2502 | } 2503 | }, 2504 | "url-to-options": { 2505 | "version": "1.0.1", 2506 | "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", 2507 | "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" 2508 | }, 2509 | "util-deprecate": { 2510 | "version": "1.0.2", 2511 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2512 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 2513 | }, 2514 | "utils-merge": { 2515 | "version": "1.0.1", 2516 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 2517 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 2518 | }, 2519 | "uuid": { 2520 | "version": "2.0.3", 2521 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", 2522 | "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" 2523 | }, 2524 | "vary": { 2525 | "version": "1.1.2", 2526 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 2527 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 2528 | }, 2529 | "walkdir": { 2530 | "version": "0.0.11", 2531 | "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz", 2532 | "integrity": "sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI=" 2533 | }, 2534 | "whatwg-fetch": { 2535 | "version": "2.0.4", 2536 | "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", 2537 | "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" 2538 | }, 2539 | "which": { 2540 | "version": "1.3.1", 2541 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 2542 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 2543 | "requires": { 2544 | "isexe": "2.0.0" 2545 | } 2546 | }, 2547 | "widest-line": { 2548 | "version": "2.0.0", 2549 | "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", 2550 | "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", 2551 | "requires": { 2552 | "string-width": "2.1.1" 2553 | }, 2554 | "dependencies": { 2555 | "ansi-regex": { 2556 | "version": "3.0.0", 2557 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 2558 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 2559 | }, 2560 | "is-fullwidth-code-point": { 2561 | "version": "2.0.0", 2562 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 2563 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 2564 | }, 2565 | "string-width": { 2566 | "version": "2.1.1", 2567 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 2568 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 2569 | "requires": { 2570 | "is-fullwidth-code-point": "2.0.0", 2571 | "strip-ansi": "4.0.0" 2572 | } 2573 | }, 2574 | "strip-ansi": { 2575 | "version": "4.0.0", 2576 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 2577 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 2578 | "requires": { 2579 | "ansi-regex": "3.0.0" 2580 | } 2581 | } 2582 | } 2583 | }, 2584 | "wrappy": { 2585 | "version": "1.0.2", 2586 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2587 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 2588 | }, 2589 | "write-file-atomic": { 2590 | "version": "2.3.0", 2591 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", 2592 | "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", 2593 | "requires": { 2594 | "graceful-fs": "4.1.11", 2595 | "imurmurhash": "0.1.4", 2596 | "signal-exit": "3.0.2" 2597 | } 2598 | }, 2599 | "xdg-basedir": { 2600 | "version": "3.0.0", 2601 | "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", 2602 | "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" 2603 | }, 2604 | "xml2js": { 2605 | "version": "0.4.17", 2606 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", 2607 | "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", 2608 | "requires": { 2609 | "sax": "1.2.1", 2610 | "xmlbuilder": "4.2.1" 2611 | } 2612 | }, 2613 | "xmlbuilder": { 2614 | "version": "4.2.1", 2615 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", 2616 | "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", 2617 | "requires": { 2618 | "lodash": "4.17.10" 2619 | } 2620 | }, 2621 | "xtend": { 2622 | "version": "4.0.1", 2623 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 2624 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 2625 | }, 2626 | "yallist": { 2627 | "version": "2.1.2", 2628 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 2629 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" 2630 | }, 2631 | "yaml-ast-parser": { 2632 | "version": "0.0.34", 2633 | "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.34.tgz", 2634 | "integrity": "sha1-0A88+ddztyQUCa6SpnQNHbGfSeY=" 2635 | }, 2636 | "yauzl": { 2637 | "version": "2.10.0", 2638 | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", 2639 | "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", 2640 | "requires": { 2641 | "buffer-crc32": "0.2.13", 2642 | "fd-slicer": "1.1.0" 2643 | } 2644 | }, 2645 | "zip-stream": { 2646 | "version": "1.2.0", 2647 | "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.2.0.tgz", 2648 | "integrity": "sha1-qLxF9MG0lpnGuQGYuqyqzbzUugQ=", 2649 | "requires": { 2650 | "archiver-utils": "1.3.0", 2651 | "compress-commons": "1.2.2", 2652 | "lodash": "4.17.10", 2653 | "readable-stream": "2.3.6" 2654 | } 2655 | } 2656 | } 2657 | } 2658 | -------------------------------------------------------------------------------- /03-videolyzer/videolyzer/serverless.yml: -------------------------------------------------------------------------------- 1 | service: videolyzer 2 | 3 | provider: 4 | name: aws 5 | runtime: python3.6 6 | profile: ${file(../config.${self:provider.stage}.json):videolyzer.profile} 7 | 8 | iamRoleStatements: 9 | - Effect: "Allow" 10 | Action: 11 | - "rekognition:*" 12 | Resource: "*" 13 | - Effect: "Allow" 14 | Action: 15 | - "s3:GetObject" 16 | Resource: 17 | - Fn::Join: 18 | - '' 19 | - - ${self:custom.videosBucketArn} 20 | - '/*' 21 | - Effect: "Allow" 22 | Action: 23 | - iam:GetRole 24 | - iam:PassRole 25 | Resource: 26 | Fn::GetAtt: 27 | - RekognitionSNSPublishRole 28 | - Arn 29 | - Effect: "Allow" 30 | Action: 31 | - "dynamodb:PutItem" 32 | Resource: 33 | Fn::GetAtt: 34 | - VideosTable 35 | - Arn 36 | 37 | environment: 38 | DYNAMODB_TABLE_NAME: ${self:custom.videosTableName} 39 | REKOGNITION_SNS_TOPIC_ARN: ${self:custom.rekognitionSNSTopicArn} 40 | REKOGNITION_ROLE_ARN: 41 | Fn::GetAtt: 42 | - RekognitionSNSPublishRole 43 | - Arn 44 | 45 | custom: 46 | videosTableName: ${file(../config.${self:provider.stage}.json):videolyzer.videos_table} 47 | rekognitionSNSTopicArn: 48 | Fn::Join: 49 | - ':' 50 | - - arn 51 | - aws 52 | - sns 53 | - Ref: AWS::Region 54 | - Ref: AWS::AccountId 55 | - handleLabelDetectionTopic 56 | videosBucketArn: 57 | Fn::Join: 58 | - ':' 59 | - - arn 60 | - aws 61 | - s3 62 | - '' 63 | - '' 64 | - ${file(../config.${self:provider.stage}.json):videolyzer.videos_bucket} 65 | 66 | functions: 67 | startProcessingVideo: 68 | handler: handler.start_processing_video 69 | events: 70 | - s3: 71 | bucket: ${file(../config.${self:provider.stage}.json):videolyzer.videos_bucket} 72 | event: s3:ObjectCreated:* 73 | rules: 74 | - suffix: .mp4 75 | handleLabelDetection: 76 | handler: handler.handle_label_detection 77 | events: 78 | - sns: handleLabelDetectionTopic 79 | 80 | resources: 81 | Resources: 82 | VideosTable: 83 | Type: AWS::DynamoDB::Table 84 | Properties: 85 | AttributeDefinitions: 86 | - 87 | AttributeName: videoName 88 | AttributeType: S 89 | KeySchema: 90 | - 91 | AttributeName: videoName 92 | KeyType: HASH 93 | ProvisionedThroughput: 94 | ReadCapacityUnits: 1 95 | WriteCapacityUnits: 1 96 | TableName: ${self:custom.videosTableName} 97 | RekognitionSNSPublishRole: 98 | Type: AWS::IAM::Role 99 | Properties: 100 | AssumeRolePolicyDocument: 101 | Version: '2012-10-17' 102 | Statement: 103 | - Effect: Allow 104 | Principal: 105 | Service: 106 | - rekognition.amazonaws.com 107 | Action: 108 | - sts:AssumeRole 109 | Policies: 110 | - PolicyName: RekognitionSNSPublishPolicy 111 | PolicyDocument: 112 | Version: '2012-10-17' 113 | Statement: 114 | - Effect: Allow 115 | Action: 116 | - sns:Publish 117 | Resource: ${self:custom.rekognitionSNSTopicArn} 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automating AWS with Python 2 | 3 | Repository for the A Cloud Guru course *Automating AWS with Python* 4 | 5 | ## 01-webotron 6 | 7 | Webotron is a script that will sync a local directory to an s3 bucket, and optionally configure Route 53 and cloudfront as well. 8 | 9 | ### Features 10 | 11 | Webotron currently has the following features: 12 | 13 | - List bucket 14 | - List contents of a bucket 15 | - Create and set up bucket 16 | - Sync directory tree to bucket 17 | - Set AWS profile with --profile= 18 | - Configure route 53 domain 19 | 20 | ## 02-notifon 21 | 22 | Notifon is a project to notify Slack users of changes to your AWS account using CloudWatch Events 23 | 24 | ### Features 25 | 26 | Notifon currently has the following features: 27 | 28 | - Send notifications to Slack when cloudwatch events happen 29 | --------------------------------------------------------------------------------