diff --git a/.circleci/config.yml b/.circleci/config.yml index 49935061..2217e820 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ version: 2 jobs: ui-build: docker: - - image: circleci/node:7.10 + - image: circleci/node:8.10 working_directory: ~/carpal/frontend @@ -49,10 +49,13 @@ jobs: - run: name: Deploy to S3 command: aws s3 sync /tmp/build/ s3://carpal-ui-rides-dev/ --delete - services-deploy: + services-build: docker: - - image: circleci/node:7.10 - working_directory: ~/carpal/infrastructure/services + - image: circleci/node:8.10 + - image: mysql:5.7 + environment: + MYSQL_ROOT_PASSWORD: admin + working_directory: ~/carpal/backend steps: - checkout: path: ~/carpal @@ -61,17 +64,40 @@ jobs: - v1-dependencies-{{ checksum "package.json" }} # fallback to using the latest cache if no exact match is found - v1-dependencies- + - run: + name: install dockerize + command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz + environment: + DOCKERIZE_VERSION: v0.6.1 + - run: + name: Wait for db + command: dockerize -wait tcp://localhost:3306 -timeout 1m - run: npm install + - run: npm run refresh-db + - run: npm test - save_cache: paths: - node_modules key: v1-dependencies-{{ checksum "package.json" }} + services-deploy: + docker: + - image: circleci/node:8.10 + working_directory: ~/carpal/backend + steps: + - checkout: + path: ~/carpal + - restore_cache: + keys: + - v1-dependencies-{{ checksum "package.json" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- - run: ./writeSecrets.sh - - run: npm run deploy-sls + - run: npm run deploy-sls workflows: version: 2 - ui-build-deploy: + application-build-deploy: jobs: + - services-build - ui-build - ui-deploy: requires: @@ -82,4 +108,4 @@ workflows: - services-deploy: filters: branches: - only: master \ No newline at end of file + only: master diff --git a/README.md b/README.md index 79c08840..4d88193f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ # hills-carpal [![CircleCI](https://circleci.com/gh/RHoKAustralia/hills-carpal.svg?style=svg)](https://circleci.com/gh/RHoKAustralia/hills-carpal) + +# Frontend + + + diff --git a/infrastructure/services/.gitignore b/backend/.gitignore similarity index 100% rename from infrastructure/services/.gitignore rename to backend/.gitignore diff --git a/infrastructure/services/package-lock.json b/backend/package-lock.json similarity index 91% rename from infrastructure/services/package-lock.json rename to backend/package-lock.json index 55ddd226..1a6d949f 100644 --- a/infrastructure/services/package-lock.json +++ b/backend/package-lock.json @@ -27,7 +27,6 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "dev": true, "requires": { "mime-types": "~2.1.18", "negotiator": "0.6.1" @@ -186,8 +185,7 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "array-union": { "version": "1.0.2", @@ -199,10 +197,9 @@ } }, "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz", + "integrity": "sha1-X8w3OSB3VyPP1k1lxkvvU7+eum0=" }, "asn1": { "version": "0.2.3", @@ -214,6 +211,12 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -358,16 +361,15 @@ } }, "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", + "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==", "dev": true }, "body-parser": { "version": "1.18.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", - "dev": true, "requires": { "bytes": "3.0.0", "content-type": "~1.0.4", @@ -379,6 +381,21 @@ "qs": "6.5.2", "raw-body": "2.3.3", "type-is": "~1.6.16" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "boxen": { @@ -439,6 +456,12 @@ "concat-map": "0.0.1" } }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "buffer": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", @@ -492,8 +515,7 @@ "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, "camelcase": { "version": "4.1.0", @@ -502,9 +524,9 @@ "dev": true }, "capture-stack-trace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", - "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", "dev": true }, "caseless": { @@ -524,6 +546,26 @@ "url-to-options": "^1.0.1" } }, + "chai": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", + "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", + "dev": true, + "requires": { + "assertion-error": "^1.0.1", + "check-error": "^1.0.1", + "deep-eql": "^3.0.0", + "get-func-name": "^2.0.0", + "pathval": "^1.0.0", + "type-detect": "^4.0.0" + } + }, + "chai-exclude": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/chai-exclude/-/chai-exclude-1.0.9.tgz", + "integrity": "sha512-lnlzkl7OKelBYWvY6ewFJsE3pIExIrPl5Hu8dUGPk+CWBiD7H3B/RBaeC8XXC1xypdYNNB5Zi6qi5MpacO8Lyw==", + "dev": true + }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -535,10 +577,16 @@ "supports-color": "^5.3.0" } }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, "ci-info": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.3.1.tgz", - "integrity": "sha512-l4wK/SFEN8VVTQ9RO1I5yzIL2vw1w6My29qA6Gwaec80QeHxfXbruuUWqn1knyMoJn/X5kav3zVY1TlRHSKeIA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.4.0.tgz", + "integrity": "sha512-Oqmw2pVfCl8sCL+1QgMywPfdxPJPkC51y4usw0iiE2S9qnEOAqXy8bwl1CpMpnoU39g4iKJTz6QZj+28FvOnjQ==", "dev": true }, "cli-boxes": { @@ -574,18 +622,18 @@ "dev": true }, "color-convert": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", - "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { - "color-name": "1.1.1" + "color-name": "1.1.3" } }, "color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "combined-stream": { @@ -597,13 +645,10 @@ } }, "commander": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", - "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", - "dev": true, - "requires": { - "graceful-readlink": ">= 1.0.0" - } + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true }, "component-emitter": { "version": "1.2.1", @@ -668,26 +713,22 @@ "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "cookie": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "cookiejar": { "version": "2.1.2", @@ -726,9 +767,9 @@ }, "dependencies": { "buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.0.tgz", - "integrity": "sha512-nUJyfChH7PMJy75eRDCCKtszSEFokUNXC1hNVSe+o+VdcgvDPLs20k3v8UXI8ruRYAJiYtyRea8mYyqPxoHWDw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", "dev": true, "requires": { "base64-js": "^1.0.2", @@ -782,9 +823,9 @@ } }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -887,6 +928,15 @@ } } }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -907,13 +957,17 @@ "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" }, "destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, "dot-prop": { @@ -966,14 +1020,12 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, "encoding": { "version": "0.1.12", @@ -1011,8 +1063,7 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "escape-string-regexp": { "version": "1.0.5", @@ -1029,8 +1080,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, "events": { "version": "1.1.1", @@ -1063,7 +1113,6 @@ "version": "4.16.3", "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", - "dev": true, "requires": { "accepts": "~1.3.5", "array-flatten": "1.1.1", @@ -1101,7 +1150,6 @@ "version": "1.18.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "dev": true, "requires": { "bytes": "3.0.0", "content-type": "~1.0.4", @@ -1115,23 +1163,33 @@ "type-is": "~1.6.15" } }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", - "dev": true + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" }, "raw-body": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "dev": true, "requires": { "bytes": "3.0.0", "http-errors": "1.6.2", @@ -1142,14 +1200,12 @@ "depd": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", - "dev": true + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" }, "http-errors": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "dev": true, "requires": { "depd": "1.1.1", "inherits": "2.0.3", @@ -1160,22 +1216,14 @@ "setprototypeof": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", - "dev": true + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" } } }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" } } }, @@ -1186,7 +1234,7 @@ }, "external-editor": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-1.1.1.tgz", "integrity": "sha1-Etew24UPf/fnCBuvQAVwAGDEYAs=", "dev": true, "requires": { @@ -1268,7 +1316,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "dev": true, "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -1279,11 +1326,18 @@ "unpipe": "~1.0.0" }, "dependencies": { - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -1311,14 +1365,12 @@ "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "fs-constants": { "version": "1.0.0", @@ -1358,6 +1410,12 @@ "lodash.padstart": "^4.1.0" } }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, "get-proxy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", @@ -1425,7 +1483,7 @@ }, "got": { "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz", "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { @@ -1463,6 +1521,12 @@ "lodash": "^4.11.1" } }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -1513,11 +1577,16 @@ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, "requires": { "depd": "~1.1.2", "inherits": "2.0.3", @@ -1543,30 +1612,12 @@ "requires": { "agent-base": "^4.1.0", "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } } }, "iconv-lite": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -1640,7 +1691,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -1662,8 +1713,7 @@ "ipaddr.js": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", - "dev": true + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" }, "is-ci": { "version": "1.2.0", @@ -1844,14 +1894,6 @@ "path-loader": "^1.0.2", "slash": "^1.0.0", "uri-js": "^3.0.2" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - } } }, "json-schema": { @@ -2076,26 +2118,22 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "mime": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" }, "mime-db": { "version": "1.33.0", @@ -2120,9 +2158,9 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mkdirp": { @@ -2132,21 +2170,31 @@ "dev": true, "requires": { "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } + } + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" } }, "moment": { "version": "2.22.2", "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", - "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=", - "dev": true + "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" }, "ms": { "version": "2.1.1", @@ -2186,8 +2234,7 @@ "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, "node-fetch": { "version": "1.7.3", @@ -2273,7 +2320,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, "requires": { "ee-first": "1.1.1" } @@ -2335,8 +2381,7 @@ "parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", - "dev": true + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, "path-is-absolute": { "version": "1.0.1", @@ -2357,9 +2402,9 @@ "dev": true }, "path-loader": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-loader/-/path-loader-1.0.6.tgz", - "integrity": "sha512-vAtndQsgWS0s2JOjT+NWtJyP5Gc940SlQQ55j0+qSj/SJQ4dmt/L8gLeW9wJF0rM32qEts+3NDvKjs6TUxwFtg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/path-loader/-/path-loader-1.0.8.tgz", + "integrity": "sha512-/JQCrTcrteaPB8IHefEAQbmBQReKj51A+yTyc745TBbO4FOySw+/l3Rh0zyad0Nrd87TMROlmFANQwCRsuvN4w==", "dev": true, "requires": { "native-promise-only": "^0.8.1", @@ -2369,7 +2414,12 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", "dev": true }, "pend": { @@ -2431,7 +2481,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", - "dev": true, "requires": { "forwarded": "~0.1.2", "ipaddr.js": "1.8.0" @@ -2466,11 +2515,18 @@ "integrity": "sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==", "dev": true }, + "randomstring": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/randomstring/-/randomstring-1.1.5.tgz", + "integrity": "sha1-bfBij3XL1ZMpMNn+OrTpVqGFGMM=", + "requires": { + "array-uniq": "1.0.2" + } + }, "range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" }, "raven": { "version": "1.2.1", @@ -2497,7 +2553,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "dev": true, "requires": { "bytes": "3.0.0", "http-errors": "1.6.3", @@ -2515,6 +2570,14 @@ "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } } }, "readable-stream": { @@ -2637,8 +2700,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sax": { "version": "1.2.1", @@ -2653,6 +2715,17 @@ "dev": true, "requires": { "commander": "~2.8.1" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "dev": true, + "requires": { + "graceful-readlink": ">= 1.0.0" + } + } } }, "semver": { @@ -2680,7 +2753,6 @@ "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "dev": true, "requires": { "debug": "2.6.9", "depd": "~1.1.2", @@ -2697,17 +2769,18 @@ "statuses": "~1.4.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -2715,7 +2788,6 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "dev": true, "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -2724,9 +2796,9 @@ } }, "serverless": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/serverless/-/serverless-1.30.1.tgz", - "integrity": "sha512-+Ed6HNhJl/j22a3muXXBchDtK3HTpq2kKx/v8b181p9SXQpX+kWmgGfJzw8kK9Z/1KQvH0d0Nm4nYeKprqe8MQ==", + "version": "1.30.3", + "resolved": "https://registry.npmjs.org/serverless/-/serverless-1.30.3.tgz", + "integrity": "sha512-Jn5L/aZMyp7ZbrZPq/sVRDgzDtxPmLJo766unDy3IExtzBOT/AdGe7m0nFIU7njcXR9Ol7swtR2zFmyS3ehOAA==", "dev": true, "requires": { "@serverless/platform-sdk": "^0.2.1", @@ -2767,6 +2839,12 @@ "yaml-ast-parser": "0.0.34" }, "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, "uuid": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", @@ -2778,8 +2856,7 @@ "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, "shebang-command": { "version": "1.2.0", @@ -2815,9 +2892,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.8.tgz", - "integrity": "sha512-WqAEWPdb78u25RfKzOF0swBpY0dKrNdjc4GvLwm7ScX/o9bj8Eh/YL8mcMhBHYDGl87UkkSXDOFnW4G7GhWhGg==", + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -2867,10 +2944,9 @@ "dev": true }, "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" }, "string-width": { "version": "1.0.2", @@ -2948,21 +3024,6 @@ "readable-stream": "^2.3.5" }, "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -3018,6 +3079,29 @@ "mkdirp": "^0.5.1", "npmlog": "^2.0.3", "object-assign": "^4.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "tar-stream": { @@ -3109,11 +3193,16 @@ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "optional": true }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "dev": true, "requires": { "media-typer": "0.3.0", "mime-types": "~2.1.18" @@ -3143,7 +3232,7 @@ }, "buffer": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz", "integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=", "dev": true, "requires": { @@ -3166,8 +3255,7 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "unzip-response": { "version": "2.0.1", @@ -3243,8 +3331,7 @@ "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { "version": "3.2.1", @@ -3254,8 +3341,7 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, "verror": { "version": "1.10.0", diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 00000000..6058113e --- /dev/null +++ b/backend/package.json @@ -0,0 +1,28 @@ +{ + "name": "carpal", + "version": "1.0.0", + "scripts": { + "test": "mocha './src/test/**/*.test.js'", + "start": "node src/test/expressApis.js", + "refresh-db": "node ./src/main/database/index.js", + "deploy-sls": "sls deploy" + }, + "dependencies": { + "body-parser": "^1.18.3", + "express": "^4.16.3", + "jsonschema": "^1.2.4", + "jsonwebtoken": "^8.1.0", + "moment": "^2.22.2", + "mysql": "^2.15.0", + "randomstring": "^1.1.5", + "request": "^2.87.0", + "uuid": "^3.2.1" + }, + "devDependencies": { + "aws-sdk": "^2.250.1", + "chai": "^4.1.2", + "chai-exclude": "^1.0.9", + "mocha": "^5.2.0", + "serverless": "^1.30.1" + } +} diff --git a/infrastructure/services/props.json b/backend/props.json similarity index 100% rename from infrastructure/services/props.json rename to backend/props.json diff --git a/infrastructure/services/public_key b/backend/public_key similarity index 98% rename from infrastructure/services/public_key rename to backend/public_key index 7cb399d7..3aef10fc 100644 --- a/infrastructure/services/public_key +++ b/backend/public_key @@ -1,19 +1,19 @@ ------BEGIN CERTIFICATE----- -MIIDATCCAemgAwIBAgIJRppYVkre6ervMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNV -BAMTE2NhcnBhbC5hdS5hdXRoMC5jb20wHhcNMTgwNjAyMDMyOTA5WhcNMzIwMjA5 -MDMyOTA5WjAeMRwwGgYDVQQDExNjYXJwYWwuYXUuYXV0aDAuY29tMIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqvdMuabJiGr2ntuurtTZB/Mu6GNVyBQN -tnDWjktZIkPKqDiIyl2xxbufIpIw4qEQjOUlJDg4YSi8K9T/mCv+wRes65cjQcw2 -x0TNlZtdZ6vG8luWVWLvNE+DjqmsdMIgiLNirdKzNL3Ku2VvuFFvwcBAmXsqORT8 -LcLOE2etTqdPuBdC0sGzAMncf+3J+BxLoZNCpxJUsh1vbw6mz6/TJjGmmBzOC70o -LyXX9K+OevsW0bLkZeOJZAsGxDWDJ8B31TOarzvko66IlGukUSmmcg/PPiWdBjp5 -IHdjjSjKVdApVrZmltjR7glh3RswVrvqEKGa7dH62928alo2yi0zRwIDAQABo0Iw -QDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTqR7LijtYwkBuqB8txw+TDugz+ -6TAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADggEBAC9tL9V5NOs1OFkg -wEzRynYByZZpij2YHhZmnJxuIhdHKBf2+/AXcq4zN78y97D6lfdXVueJrB5OSm3R -6yhffgY7JSURIE96LfEjoh63Y7Viy1Pdk4A9aYWvpoAjlnIb+s3vg+13ChH4b5p5 -m4w3hOU8jJV2VoGooiTAWeHJsUYUPsMLneVKV7wv8Wl7sgLvO+xeRKpa+Sc71Cym -dLGt3NP+EP9zhFRPxpaCIW3rsMnEgSIuekFmVqzv7exMy+srY2V3p0uqiCHdjDQo -ZoumDRlverqMTWICzZsCRRpT4XcM4NlUMLw+zkQtymmDJGIBkZ7pxF+OfQXLRCHr -bOPxM6M= ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDATCCAemgAwIBAgIJRppYVkre6ervMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNV +BAMTE2NhcnBhbC5hdS5hdXRoMC5jb20wHhcNMTgwNjAyMDMyOTA5WhcNMzIwMjA5 +MDMyOTA5WjAeMRwwGgYDVQQDExNjYXJwYWwuYXUuYXV0aDAuY29tMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqvdMuabJiGr2ntuurtTZB/Mu6GNVyBQN +tnDWjktZIkPKqDiIyl2xxbufIpIw4qEQjOUlJDg4YSi8K9T/mCv+wRes65cjQcw2 +x0TNlZtdZ6vG8luWVWLvNE+DjqmsdMIgiLNirdKzNL3Ku2VvuFFvwcBAmXsqORT8 +LcLOE2etTqdPuBdC0sGzAMncf+3J+BxLoZNCpxJUsh1vbw6mz6/TJjGmmBzOC70o +LyXX9K+OevsW0bLkZeOJZAsGxDWDJ8B31TOarzvko66IlGukUSmmcg/PPiWdBjp5 +IHdjjSjKVdApVrZmltjR7glh3RswVrvqEKGa7dH62928alo2yi0zRwIDAQABo0Iw +QDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTqR7LijtYwkBuqB8txw+TDugz+ +6TAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADggEBAC9tL9V5NOs1OFkg +wEzRynYByZZpij2YHhZmnJxuIhdHKBf2+/AXcq4zN78y97D6lfdXVueJrB5OSm3R +6yhffgY7JSURIE96LfEjoh63Y7Viy1Pdk4A9aYWvpoAjlnIb+s3vg+13ChH4b5p5 +m4w3hOU8jJV2VoGooiTAWeHJsUYUPsMLneVKV7wv8Wl7sgLvO+xeRKpa+Sc71Cym +dLGt3NP+EP9zhFRPxpaCIW3rsMnEgSIuekFmVqzv7exMy+srY2V3p0uqiCHdjDQo +ZoumDRlverqMTWICzZsCRRpT4XcM4NlUMLw+zkQtymmDJGIBkZ7pxF+OfQXLRCHr +bOPxM6M= +-----END CERTIFICATE----- diff --git a/backend/readme.md b/backend/readme.md new file mode 100644 index 00000000..501e24ce --- /dev/null +++ b/backend/readme.md @@ -0,0 +1,54 @@ +## Initial Setup + +### Database +The project holds sql scripts that can be used to incrementally apply changes to the database. +You can therefore also use this scripts to initialize your local database. +To run it: +``` +npm run refresh-db +``` +By default it will try to connect to a local mysql database with username `root` and password `admin`. If you want to change those configurations you can set as environment variable, for instance: +``` +MYSQL_USER=myuser MYSQL_PW=myPassword MYSQL_HOST=myHost MYSQL_PORT=3316 MYSQL_DB=myDB npm run refresh-db +``` + +### Running the application locally + +The application was designed to work with AWS lambdas. Although an *expressJs* layer was added to be able to run the application locally. To start it: + +``` +npm start +``` +**or** to be able to debug it: +``` +node ./backend/src/test/expressApis.js +``` + +**Attention**: The application and tests make use of the database, therefore make sure that you have your environment variables set to configure them to run against the right mysql instance. + + +### Running the tests +In the `backend/` directory run: + +``` +npm test +``` + +### Deploying with serverless +Run the following in the ./infrastructure/services directory to create the API Gateway config and lambdas: +``` +npm install +npm install serverless -g +serverless deploy +``` + +### API Gateway Custom Domain +Services are published on api.carpal.org.au. + +This is achieved using: +1. An SSL certificate in North Virginia for the domain +2. A configuration in API Gateway for the custom domain as below: +![image](api-gateway-custom-doman.png) +3. A Route53 entry for api.carpal.org.au is ALIAS'd (like a hidden CNAME) to the above Target Domain Name. + +Ref: https://docs.aws.amazon.com/console/apigateway/custom-domains diff --git a/infrastructure/services/serverless.yml b/backend/serverless.yml similarity index 77% rename from infrastructure/services/serverless.yml rename to backend/serverless.yml index 3f6665c0..08e6a2b1 100644 --- a/infrastructure/services/serverless.yml +++ b/backend/serverless.yml @@ -2,15 +2,18 @@ service: carpal provider: name: aws - runtime: nodejs6.10 + runtime: nodejs8.10 region: ap-southeast-2 stage: dev memorySize: 128 timeout: 30 environment: AUTH0_CLIENT_ID: ${file(./props.json):AUTH0_CLIENT_ID} + MYSQL_HOST: carpal.cttgjqpjknhf.ap-southeast-2.rds.amazonaws.com MYSQL_PW: ${file(./secrets.json):MYSQL_PW} MYSQL_PORT: ${file(./secrets.json):MYSQL_PORT} + MYSQL_USER: carpaladmin + DOMAIN: carpal.org.au AUTH0_CLIENT_PUBLIC_KEY: ${file(./public_key)} vpc: @@ -23,16 +26,16 @@ provider: functions: auth: - handler: auth/auth.auth + handler: src/main/auth/auth.auth cors: true currentTime: - handler: utils/ping.endpoint + handler: src/main/utils/ping.endpoint events: - http: path: ping method: get loggedin: - handler: auth/loggedin.loggedin + handler: src/main/auth/loggedin.loggedin events: - http: path: authcheck @@ -40,7 +43,7 @@ functions: authorizer: auth cors: true create: - handler: rides/create.create + handler: src/main/rides/awsLambdaRidesApis.create events: - http: path: rides @@ -48,7 +51,7 @@ functions: cors: true update: - handler: rides/update.update + handler: src/main/rides/awsLambdaRidesApis.update events: - http: path: rides/{id} @@ -60,7 +63,7 @@ functions: id: true list: - handler: rides/list.list + handler: src/main/rides/awsLambdaRidesApis.list # runtime: nodejs8.10 events: - http: @@ -69,7 +72,7 @@ functions: cors: true findone: - handler: rides/findone.findone + handler: src/main/rides/awsLambdaRidesApis.findOne events: - http: path: rides/{id} @@ -79,13 +82,11 @@ functions: parameters: paths: id: true - maps: - handler: map/maps.maps - events: - - http: - path: map - method: get - cors: true +# notify: +# handler: notify +# events: +# - schedule: rate(2 hours) +# - schedule: cron(0 12 * * ? *) resources: Resources: GatewayResponse: diff --git a/infrastructure/services/auth/auth.js b/backend/src/main/auth/auth.js similarity index 100% rename from infrastructure/services/auth/auth.js rename to backend/src/main/auth/auth.js diff --git a/infrastructure/services/auth/loggedin.js b/backend/src/main/auth/loggedin.js similarity index 100% rename from infrastructure/services/auth/loggedin.js rename to backend/src/main/auth/loggedin.js diff --git a/backend/src/main/database/DatabaseManager.js b/backend/src/main/database/DatabaseManager.js new file mode 100644 index 00000000..97221fd6 --- /dev/null +++ b/backend/src/main/database/DatabaseManager.js @@ -0,0 +1,93 @@ +const mysql = require('mysql'); + +class DatabaseManager { + + constructor(databaseConfig) { + this.databaseConfig = databaseConfig || { + host: process.env.MYSQL_HOST || 'localhost', + port: process.env.MYSQL_PORT || 3306, + user: process.env.MYSQL_USER || 'root', + password: process.env.MYSQL_PW || 'admin', + database: process.env.MYSQL_DB || 'carpal', + multipleStatements: true + }; + } + + createConnection() { + return mysql.createConnection(this.databaseConfig); + } + + query(queryString, connection) { + let closeConnection = false; + if (!connection) { + connection = this.createConnection(); + closeConnection = true; + } + + return new Promise((resolve, reject) => { + connection.query(queryString, (error, results, fields) => { + if (closeConnection) { + let closePromise = this.closeConnection(connection); + return closePromise + .then(() => { + resolve(results); + }) + .catch(error1 => { + reject(error1 || error); + }); + } + if (error) { + console.log("Error executing", queryString, error); + return reject(error); + } + return resolve(results); + }); + }); + } + + closeConnection(connection) { + return new Promise((resolve, reject) => { + connection.end(function (error) { + if (error) { + return reject(error); + } + resolve(); + }); + }); + } + + beginTransaction(connection) { + return new Promise((resolve, reject) => { + connection.beginTransaction(function (error) { + if (error) { + return reject(error); + } + resolve(); + }); + }); + } + + rollback(connection) { + return new Promise((resolve, reject) => { + connection.rollback(function (error) { + if (error) { + return reject(error); + } + resolve(); + }); + }); + } + + commit(connection) { + return new Promise((resolve, reject) => { + connection.commit(function (error) { + if (error) { + return reject(error); + } + resolve(); + }); + }); + } +} + +module.exports = DatabaseManager; \ No newline at end of file diff --git a/backend/src/main/database/RefreshDatabase.js b/backend/src/main/database/RefreshDatabase.js new file mode 100644 index 00000000..193d4486 --- /dev/null +++ b/backend/src/main/database/RefreshDatabase.js @@ -0,0 +1,40 @@ +const fs = require('fs'); +const path = require('path'); +const DatabaseManager = require('./DatabaseManager'); + +const createDB = '2018-08-04-1-create-database.sql'; +const changeSet = [ + '2018-08-04-2-create-location-table.sql', + '2018-08-04-3-create-rides-table.sql', + '2018-08-04-4-create-driver-table.sql', + '2018-08-04-5-create-driver_car-table.sql', + '2018-08-04-6-create-driver_ride-table.sql' +]; + +class RefreshDatabase { + constructor(databaseManager) { + const dbConfig = Object.assign({}, databaseManager.databaseConfig); + + delete dbConfig.database; + this.databaseManagerNoDb = new DatabaseManager(dbConfig); + this.databaseManager = databaseManager; + } + + async executeAll() { + this.execute(createDB, this.databaseManagerNoDb) + .then(() => { + let result = Promise.resolve(); + changeSet.forEach(fileName => { + result = result.then(() => this.execute(fileName, this.databaseManager)) + }); + return result; + }) + } + + execute(fileName, databaseManager) { + let sql = fs.readFileSync(path.resolve(__dirname, './changes/' + fileName)).toString().trim(); + return databaseManager.query(sql); + } +} + +module.exports = RefreshDatabase; \ No newline at end of file diff --git a/backend/src/main/database/changes/2018-08-04-1-create-database.sql b/backend/src/main/database/changes/2018-08-04-1-create-database.sql new file mode 100644 index 00000000..62719596 --- /dev/null +++ b/backend/src/main/database/changes/2018-08-04-1-create-database.sql @@ -0,0 +1 @@ +CREATE SCHEMA IF NOT EXISTS carpal; \ No newline at end of file diff --git a/backend/src/main/database/changes/2018-08-04-2-create-location-table.sql b/backend/src/main/database/changes/2018-08-04-2-create-location-table.sql new file mode 100644 index 00000000..884ad0a3 --- /dev/null +++ b/backend/src/main/database/changes/2018-08-04-2-create-location-table.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS carpal.location ( + id INT(11), + location GEOMETRY NOT NULL, + suburb TEXT, + placeName TEXT, + postCode VARCHAR(10) +); \ No newline at end of file diff --git a/backend/src/main/database/changes/2018-08-04-3-create-rides-table.sql b/backend/src/main/database/changes/2018-08-04-3-create-rides-table.sql new file mode 100644 index 00000000..c01c83f1 --- /dev/null +++ b/backend/src/main/database/changes/2018-08-04-3-create-rides-table.sql @@ -0,0 +1,21 @@ +CREATE TABLE IF NOT EXISTS carpal.rides ( + id INT(11) AUTO_INCREMENT PRIMARY KEY, + client VARCHAR(255), + facilitatorEmail VARCHAR(255), + pickupTimeAndDateInUTC DATETIME, + locationFrom POINT, + locationTo POINT, + fbLink VARCHAR(255), + driverGender VARCHAR(10), + carType VARCHAR(255), + status ENUM('OPEN','CONFIRMED','ENDED','CANCELLED') DEFAULT 'OPEN', + deleted TINYINT(4), + suburbFrom VARCHAR(255), + placeNameFrom VARCHAR(255), + postCodeFrom VARCHAR(10), + suburbTo VARCHAR(255), + placeNameTo VARCHAR(255), + postCodeTo VARCHAR(10), + description VARCHAR(1024) +); + diff --git a/backend/src/main/database/changes/2018-08-04-4-create-driver-table.sql b/backend/src/main/database/changes/2018-08-04-4-create-driver-table.sql new file mode 100644 index 00000000..18ad69f8 --- /dev/null +++ b/backend/src/main/database/changes/2018-08-04-4-create-driver-table.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS carpal.driver ( + id INT(11) PRIMARY KEY, + name VARCHAR(255), + phoneNumber VARCHAR(20), + facebookUrl VARCHAR(255) +); + diff --git a/backend/src/main/database/changes/2018-08-04-5-create-driver_car-table.sql b/backend/src/main/database/changes/2018-08-04-5-create-driver_car-table.sql new file mode 100644 index 00000000..747b7462 --- /dev/null +++ b/backend/src/main/database/changes/2018-08-04-5-create-driver_car-table.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS carpal.driver_car ( + id INT(11) PRIMARY KEY, + driver_id INT(11), + carModel VARCHAR(255), + color VARCHAR(255), + licensePlateNumber VARCHAR(255), + FOREIGN KEY (driver_id) REFERENCES driver(id) +); \ No newline at end of file diff --git a/backend/src/main/database/changes/2018-08-04-6-create-driver_ride-table.sql b/backend/src/main/database/changes/2018-08-04-6-create-driver_ride-table.sql new file mode 100644 index 00000000..44aee2b2 --- /dev/null +++ b/backend/src/main/database/changes/2018-08-04-6-create-driver_ride-table.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS carpal.driver_ride ( + id INT(11) PRIMARY KEY, + driver_id INT(11), + ride_id INT(11), + confirmed TINYINT(1), + notified24h TINYINT(1), + notified5m TINYINT(1), + FOREIGN KEY (driver_id) REFERENCES driver(id), + FOREIGN KEY (ride_id) REFERENCES rides(id) +); \ No newline at end of file diff --git a/backend/src/main/database/index.js b/backend/src/main/database/index.js new file mode 100644 index 00000000..5c3c6cd3 --- /dev/null +++ b/backend/src/main/database/index.js @@ -0,0 +1,11 @@ +const DatabaseManager = require("./DatabaseManager"); +const RefreshDatabase = require("./RefreshDatabase"); +const refreshDatabase = new RefreshDatabase(new DatabaseManager()); + +process.on('unhandledRejection', error => { + console.log('unhandledRejection', error); +}); + + +refreshDatabase.executeAll(refreshDatabase) + .catch(e => console.log(e)); \ No newline at end of file diff --git a/backend/src/main/notification/notify.js b/backend/src/main/notification/notify.js new file mode 100644 index 00000000..d67c878f --- /dev/null +++ b/backend/src/main/notification/notify.js @@ -0,0 +1,37 @@ +exports.handler = function (event, context, callback) { + + let AWS = require('aws-sdk'); + + // Create chron job + // search rides with time below 24 hours + // send sms to each one + // update entry to say that driver was notified + + // create amazon account for hillscarpal + + sendSMS(); + + +}; + +function sendSMS() { + let sns = new AWS.SNS(); + sns.publish({ + Message: 'Hello World', + PhoneNumber: '+610123456789', + MessageStructure: 'text' + }, function (err, data) { + console.log("test2") + if (err) { + console.log(err.stack); + } + + console.log('push sent'); + + callback(null, { + "statusCode": 200, + "headers": {"Date": new Date()}, + "body": "" + }); + }); +} \ No newline at end of file diff --git a/backend/src/main/rides/Coordinates.js b/backend/src/main/rides/Coordinates.js new file mode 100644 index 00000000..a2f8b7df --- /dev/null +++ b/backend/src/main/rides/Coordinates.js @@ -0,0 +1,8 @@ +class Coordinates { + constructor(latitude, longitude){ + this.latitude = latitude; + this.longitude = longitude; + } +} + +module.exports = Coordinates; \ No newline at end of file diff --git a/backend/src/main/rides/CreateRideService.js b/backend/src/main/rides/CreateRideService.js new file mode 100644 index 00000000..4533c0fe --- /dev/null +++ b/backend/src/main/rides/CreateRideService.js @@ -0,0 +1,54 @@ +'use strict'; + +const jsonValidator = require('jsonschema'); +const rideSchema = require('../schema/ride.json'); +const RideStatus = require('./RideStatus'); +const RideRepository = require('./RideRepository'); +const RideMapper = require('./RideMapper'); +const PromiseUtils = require('../utils/PromiseUtils'); + +class CreateRideService { + + constructor(databaseManager) { + this._databaseManager = databaseManager; + this._rideRepository = new RideRepository(databaseManager); + } + + createRide(body, loginData) { + const connection = this._databaseManager.createConnection(); + + const createRidePromise = this._createRide(body, loginData, connection); + const closeConnection = () => this._databaseManager.closeConnection(connection); + return PromiseUtils.promiseFinally(createRidePromise, closeConnection); + } + + _createRide(rideObject, loginData, connection) { + rideObject.datetime = new Date().getTime(); + + let validationError = this._validate(rideObject); + if (validationError) { + return Promise.reject(validationError); + } + + const payload = RideMapper.dtoToEntity(rideObject, loginData && loginData.email); + payload.status = RideStatus.OPEN; + payload.deleted = '0'; + + return this._rideRepository.create(payload, connection); + } + + _validate(data) { + const validationResult = jsonValidator.validate(data, rideSchema); + if (validationResult.errors && validationResult.errors.length) { + return { + statusCode: 400, + headers: { + 'Content-Type': 'text/plain' + }, + body: JSON.stringify({"error": validationResult.errors}) + }; + } + } +} + +module.exports = CreateRideService; \ No newline at end of file diff --git a/backend/src/main/rides/FindOneRideService.js b/backend/src/main/rides/FindOneRideService.js new file mode 100644 index 00000000..5aee8b83 --- /dev/null +++ b/backend/src/main/rides/FindOneRideService.js @@ -0,0 +1,40 @@ +'use strict'; + +const RideRepository = require('./RideRepository'); +const RidesMapper = require('./RideMapper'); +const PromiseUtils = require('../utils/PromiseUtils'); + +class FindOneRideService { + + constructor(databaseManager) { + this._databaseManager = databaseManager; + this._rideRepository = new RideRepository(databaseManager); + } + + findOne(id, loginData) { + if (loginData.role === 'driver') { + return Promise.resolve(null); + } + + const connection = this._databaseManager.createConnection(); + const findOnePromise = this._findOne(id, loginData, connection); + const closeConnection = () => this._databaseManager.closeConnection(connection); + return PromiseUtils.promiseFinally(findOnePromise, closeConnection); + } + + _findOne(id, loginData, connection) { + let jsonQuery = this._createQuery(id, loginData); + return this._rideRepository.findOne(jsonQuery, connection) + .then(item => RidesMapper.entityToDto(item)); + } + + _createQuery(id, loginData) { + return { + id: id, + facilitatorId: loginData.role === 'facilitator' ? loginData.email : undefined, + includePickupTimeInPast: true + }; + } +} + +module.exports = FindOneRideService; \ No newline at end of file diff --git a/backend/src/main/rides/ListRidesService.js b/backend/src/main/rides/ListRidesService.js new file mode 100644 index 00000000..d5ee4827 --- /dev/null +++ b/backend/src/main/rides/ListRidesService.js @@ -0,0 +1,59 @@ +'use strict'; + +const RideRepository = require('./RideRepository'); +const RidesMapper = require('./RideMapper'); +const PromiseUtils = require('../utils/PromiseUtils'); + +class ListRidesService { + + constructor(databaseManager) { + this._databaseManager = databaseManager; + this._rideRepository = new RideRepository(databaseManager); + } + + listRides(query, loginData) { + const connection = this._databaseManager.createConnection(); + + const listRidesPromise = this._listRides(query, loginData, connection) + .then(rides => rides.map(RidesMapper.entityToDto)); + const closeConnection = () => this._databaseManager.closeConnection(connection); + return PromiseUtils.promiseFinally(listRidesPromise, closeConnection); + } + + _listRides(query, loginData, connection) { + let jsonQuery = this._parseParams(query, loginData); + if (!jsonQuery) { + return Promise.resolve([]); + } + return this._rideRepository.list(jsonQuery, connection); + } + + _parseParams(query, loginData) { + const listType = query.listType || 'driver'; + const isAdmin = this._hasRole('admin', loginData); + const isDriver = this._hasRole('driver', loginData); + const isFacilitator = this._hasRole('facilitator', loginData); + const notAdminAndListTypeDoesNotMatchRole = !this._hasRole(listType, loginData) && !isAdmin; + if (notAdminAndListTypeDoesNotMatchRole) { + console.log("WARNING: unauthorised attempt to query data", loginData); + return null; + } + + return { + toLongitude: query.toLongitude, + toLatitude: query.toLatitude, + fromLongitude: query.fromLongitude, + fromLatitude: query.fromLatitude, + driverGenders: isDriver ? ['any', loginData.driverGender] : undefined, + includePickupTimeInPast: !isDriver, + facilitatorId: isFacilitator ? loginData.email : undefined, + }; + } + + _hasRole(role, loginData) { + return loginData.roles.indexOf(role) >= 0; + } +} + + +module.exports = ListRidesService; \ No newline at end of file diff --git a/backend/src/main/rides/RideMapper.js b/backend/src/main/rides/RideMapper.js new file mode 100644 index 00000000..0da9ff32 --- /dev/null +++ b/backend/src/main/rides/RideMapper.js @@ -0,0 +1,66 @@ +class RideMapper { + static entityToDto(ride) { + if(!ride){ + return null; + } + return { + "client": ride.client, + "pickupTimeAndDateInUTC": new Date(ride.pickupTimeAndDateInUTC), + "locationFrom": { + "latitude": ride.locationFrom.x, + "longitude": ride.locationFrom.y, + "suburb": ride.suburbFrom, + "postcode": ride.postCodeFrom, + "placeName": ride.placeNameFrom + }, + "locationTo": { + "latitude": ride.locationTo.x, + "longitude": ride.locationTo.y, + "suburb": ride.suburbTo, + "postcode": ride.postCodeTo, + "placeName": ride.placeNameTo + }, + "fbLink": ride.fbLink, + "driverGender": ride.driverGender, + "carType": ride.carType, + "status": ride.status, + "deleted": parseInt(ride.deleted + ''), + "facilitatorId": ride.facilitatorId || ride.facilitatorEmail, + "description": ride.description, + "id": ride.id + } + } + + static dtoToEntity(ride, facilitatorId) { + if(!ride){ + return null; + } + return { + client: `${ride.client}`, + pickupTimeAndDateInUTC: new Date(ride.pickupTimeAndDateInUTC), + locationFrom: { + latitude: ride.locationFrom.latitude, + longitude: ride.locationFrom.longitude, + suburb: ride.locationFrom.suburb, + placeName: ride.locationFrom.placeName, + postcode: ride.locationFrom.postcode, + }, + locationTo: { + latitude: ride.locationTo.latitude, + longitude: ride.locationTo.longitude, + suburb: ride.locationTo.suburb, + placeName: ride.locationTo.placeName, + postcode: ride.locationTo.postcode, + }, + fbLink: ride.fbLink, + driverGender: ride.driverGender, + carType: ride.carType, + status: ride.status, + deleted: 0, + facilitatorId: facilitatorId, + description: ride.description + } + } +} + +module.exports = RideMapper; diff --git a/backend/src/main/rides/RideRepository.js b/backend/src/main/rides/RideRepository.js new file mode 100644 index 00000000..fd2dce1b --- /dev/null +++ b/backend/src/main/rides/RideRepository.js @@ -0,0 +1,138 @@ +'use strict'; + +const moment = require('moment'); + +class RideRepository { + constructor(databaseManager) { + this._databaseManager = databaseManager; + this._dbName = this._databaseManager.databaseConfig.database; + } + + create(ride, connection) { + const escape = (data) => connection.escape(data); + const locationFrom = `POINT(${ride.locationFrom.latitude}, ${ride.locationFrom.longitude})`; + const locationTo = `POINT(${ride.locationTo.latitude}, ${ride.locationTo.longitude})`; + let query = `INSERT INTO ${this._dbName}.rides(client, + facilitatorEmail, + pickupTimeAndDateInUTC, + locationFrom, + locationTo, + fbLink, + driverGender, + carType, + status, + deleted, + suburbFrom, + placeNameFrom, + postCodeFrom, + suburbTo, + placeNameTo, + postCodeTo, + description) + VALUES + (${ + [escape(ride.client), + escape(ride.facilitatorId), + escape(moment(ride.pickupTimeAndDateInUTC).format('YYYY-MM-DD HH:mm:ss')), + locationFrom, + locationTo, + escape(ride.fbLink), + escape(ride.driverGender), + escape(ride.carType), + escape(ride.status), + escape(ride.deleted), + escape(ride.locationFrom.suburb), + escape(ride.locationFrom.placeName), + escape(ride.locationFrom.postcode), + escape(ride.locationTo.suburb), + escape(ride.locationTo.placeName), + escape(ride.locationTo.postcode), + escape(ride.description) + ].join(',')})`; + console.log(query); + + return this._databaseManager.query(query, connection); + } + + update(id, ride, connection) { + if (!id) { + throw new Error('No id specified when updating ride.'); + } + const escape = (data) => connection.escape(data); + const locationFrom = `POINT(${ride.locationFrom.latitude}, ${ride.locationFrom.longitude})`; + const locationTo = `POINT(${ride.locationTo.latitude}, ${ride.locationTo.longitude})`; + let query = `UPDATE ${this._dbName}.rides SET client = ${escape(ride.client)}, + facilitatorEmail = ${escape(ride.facilitatorId)}, + pickupTimeAndDateInUTC = ${escape(moment(ride.pickupTimeAndDateInUTC).format('YYYY-MM-DD HH:mm:ss'))}, + locationFrom = ${locationFrom}, + locationTo = ${locationTo}, + fbLink = ${escape(ride.fbLink)}, + driverGender = ${escape(ride.driverGender)}, + carType = ${escape(ride.carType)}, + status = ${escape(ride.status)}, + deleted = ${ride.deleted}, + suburbFrom = ${escape(ride.locationFrom.suburb)}, + placeNameFrom = ${escape(ride.locationFrom.placeName)}, + postCodeFrom = ${escape(ride.locationFrom.postcode)}, + suburbTo = ${escape(ride.locationTo.suburb)}, + placeNameTo = ${escape(ride.locationTo.placeName)}, + postCodeTo = ${escape(ride.locationTo.postcode)}, + description = ${escape(ride.description)} + WHERE + id = ${id}`; + console.log(query); + + return this._databaseManager.query(query, connection); + } + + findOne(jsonQuery, connection) { + return this.list(jsonQuery, connection) + .then(results => results[0] || null); + } + + /** + * @param jsonQuery: + * toLongitude {number} + * toLatitude {number} + * fromLongitude {number} + * fromLatitude {number} + * driverGenders {string[]} + * facilitatorId {string} + * includePickupTimeInPast {boolean} + * @param connection + */ + list(jsonQuery, connection) { + const escape = (data) => connection.escape(data); + let where = []; + if (jsonQuery.toLongitude && jsonQuery.toLatitude && jsonQuery.fromLongitude && jsonQuery.fromLatitude) { + where.push(`ST_Contains(ST_Envelope(ST_GeomFromText('LINESTRING(${jsonQuery.toLongitude} ${jsonQuery.toLatitude}, ${jsonQuery.fromLongitude} ${jsonQuery.fromLatitude})')), locationFrom)`); + } + if (!jsonQuery.includePickupTimeInPast) { + where.push('pickupTimeAndDateInUTC >= NOW()') + } + if (jsonQuery.id) { + where.push(`id = ${jsonQuery.id}`) + } + if (jsonQuery.driverGenders && jsonQuery.driverGenders.length) { + let genders = jsonQuery.driverGenders.map(g => ` driverGender = '${g}'`).join(' or '); + where.push(genders.length === 1 ? genders : `(${genders})`) + } + if (jsonQuery.facilitatorId) { + where.push(`facilitatorEmail = ${escape(jsonQuery.facilitatorId)}`) + } + + let query = `SELECT * FROM ${this._dbName}.rides ${where.length ? ' WHERE ' + where.join(' AND ') : ''} ORDER BY pickupTimeAndDateInUTC ASC;`; + console.log(query); + return this._databaseManager.query(query, connection) + .then(rides => + rides.map(ride => { + // Workaround to map facilitatorEmail from database to the facilitatorId in the entity + ride.facilitatorId = ride.facilitatorId || ride.facilitatorEmail; + delete ride.facilitatorEmail; + return ride; + }) + ) + } +} + +module.exports = RideRepository; diff --git a/backend/src/main/rides/RideStatus.js b/backend/src/main/rides/RideStatus.js new file mode 100644 index 00000000..6bba9867 --- /dev/null +++ b/backend/src/main/rides/RideStatus.js @@ -0,0 +1,8 @@ +const RideStatus = { + OPEN: 'OPEN', + CONFIRMED: 'CONFIRMED', + ENDED: 'ENDED', + CANCELLED: 'CANCELLED', +}; + +module.exports = RideStatus; \ No newline at end of file diff --git a/backend/src/main/rides/UpdateRideService.js b/backend/src/main/rides/UpdateRideService.js new file mode 100644 index 00000000..86f0d267 --- /dev/null +++ b/backend/src/main/rides/UpdateRideService.js @@ -0,0 +1,61 @@ +'use strict'; + +const jsonValidator = require('jsonschema'); +const rideSchema = require('../schema/ride.json'); +const RideRepository = require('./RideRepository'); +const RideMapper = require('./RideMapper'); +const PromiseUtils = require('../utils/PromiseUtils'); + +class UpdateRideService { + + constructor(databaseManager) { + this._databaseManager = databaseManager; + this._rideRepository = new RideRepository(databaseManager); + } + + updateRide(id, ride, loginData) { + const connection = this._databaseManager.createConnection(); + let updatePromise = this._updateRide(id, ride, loginData, connection); + const closeConnection = () => this._databaseManager.closeConnection(connection); + return PromiseUtils.promiseFinally(updatePromise, closeConnection); + } + + _updateRide(id, rideObject, loginData, connection) { + rideObject.datetime = new Date().getTime(); + + let validationError = this._validate(rideObject); + if (validationError) { + return Promise.reject(validationError); + } + + return this._rideRepository + .findOne({ + id: id, + facilitatorId: loginData.role === 'facilitator' ? loginData.email : undefined, + includePickupTimeInPast: true + }) + .then(ride => { + if (!ride || ride.deleted) { + return null; + } + let rideEntity = RideMapper.dtoToEntity(rideObject); + rideEntity.facilitatorId = ride.facilitatorId; + return this._rideRepository.update(ride.id, rideEntity, connection); + }); + } + + _validate(data) { + const validationResult = jsonValidator.validate(data, rideSchema); + if (validationResult.errors && validationResult.errors.length) { + return { + statusCode: 400, + headers: { + 'Content-Type': 'text/plain' + }, + body: JSON.stringify({"error": validationResult.errors}) + }; + } + } +} + +module.exports = UpdateRideService; \ No newline at end of file diff --git a/backend/src/main/rides/aws/AwsLambdaRideApis.js b/backend/src/main/rides/aws/AwsLambdaRideApis.js new file mode 100644 index 00000000..b2fa5e99 --- /dev/null +++ b/backend/src/main/rides/aws/AwsLambdaRideApis.js @@ -0,0 +1,47 @@ +const decodeJwt = require('../../utils/jwt').decodeJwt; + +class AwsLambdaRideApis { + constructor(createRideService, + listRidesService, + findOneRideService, + updateRideService) { + this.createRideService = createRideService; + this.listRidesService = listRidesService; + this.findOneRideService = findOneRideService; + this.updateRideService = updateRideService; + } + + create(event, context, callback) { + let loginData = decodeJwt(event); + return this.createRideService.createRide(JSON.parse(event.body), loginData) + .then(result => callback(null, result)) + .catch(result => callback(result)); + } + + update(event, context, callback) { + const loginData = decodeJwt(event); + const ride = JSON.parse(event.body); + const id = event.pathParameters.id; + return this.updateRideService.updateRide(id, ride, loginData) + .then(result => callback(null, result)) + .catch(result => callback(result)); + } + + list(event, context, callback) { + let loginData = decodeJwt(event); + let queryParams = event.queryStringParameters || {}; + return this.listRidesService.listRides(queryParams, loginData) + .then(result => callback(null, result)) + .catch(result => callback(result)); + } + + findOne(event, context, callback) { + let loginData = decodeJwt(event); + let pathParams = event.pathParameters || {}; + return this.findOneRideService.findOne(pathParams.id, loginData) + .then(result => callback(null, result)) + .catch(result => callback(result)); + } +} + +module.exports = AwsLambdaRideApis; \ No newline at end of file diff --git a/backend/src/main/rides/awsLambdaRidesApis.js b/backend/src/main/rides/awsLambdaRidesApis.js new file mode 100644 index 00000000..e7a2d0fe --- /dev/null +++ b/backend/src/main/rides/awsLambdaRidesApis.js @@ -0,0 +1,16 @@ +const CreateRideService = require('./CreateRideService'); +const ListRidesService = require('./ListRidesService'); +const FindOneRideService = require('./FindOneRideService'); +const DatabaseManager = require('../database/DatabaseManager'); +const AwsLambdaRideApis = require('./aws/AwsLambdaRideApis'); +const databaseManager = new DatabaseManager(); + +const createRideService = new CreateRideService(databaseManager); +const listRidesService = new ListRidesService(databaseManager); +const findOneRideService = new FindOneRideService(databaseManager); + +const rides = new AwsLambdaRideApis(createRideService, listRidesService, findOneRideService); + +module.exports = { + ...rides +}; \ No newline at end of file diff --git a/infrastructure/services/schema/ride.json b/backend/src/main/schema/ride.json similarity index 100% rename from infrastructure/services/schema/ride.json rename to backend/src/main/schema/ride.json diff --git a/backend/src/main/utils/PromiseUtils.js b/backend/src/main/utils/PromiseUtils.js new file mode 100644 index 00000000..9b3a66f5 --- /dev/null +++ b/backend/src/main/utils/PromiseUtils.js @@ -0,0 +1,17 @@ +class PromiseUtils { + static promiseFinally(promise, finallyFn) { + return new Promise((resolve, reject) => { + promise + .then(result => { + return Promise.resolve(finallyFn()) + .then(() => resolve(result)) + }) + .catch(error => { + return Promise.resolve(finallyFn()) + .then(() => reject(error)); + }); + }); + } +} + +module.exports = PromiseUtils; \ No newline at end of file diff --git a/backend/src/main/utils/jwt.js b/backend/src/main/utils/jwt.js new file mode 100644 index 00000000..4cf3d680 --- /dev/null +++ b/backend/src/main/utils/jwt.js @@ -0,0 +1,26 @@ +const jsonwebtoken = require('jsonwebtoken'); + +module.exports.decodeJwt = (event) => { + if (!event.headers.Authorization) { + return; + } + const tokenValue = event + .headers + .Authorization + .split(' ')[1]; + + try { + const domain = process.env.DOMAIN || 'carpal.org.au'; + const decodedToken = jsonwebtoken.decode(tokenValue); + const claims = {}; + claims.email = decodedToken[`https://${domain}/email`]; + claims.roles = decodedToken[`https://${domain}/roles`]; + if (claims.roles.indexOf('driver') >= 0) { + claims.driverGender = decodedToken[`https://${domain}/gender`]; + claims.car = decodedToken[`https://${domain}/car`]; + } + return claims; + } catch (err) { + console.log('catch error. Invalid token', err); + } +} \ No newline at end of file diff --git a/infrastructure/services/utils/ping.js b/backend/src/main/utils/ping.js similarity index 100% rename from infrastructure/services/utils/ping.js rename to backend/src/main/utils/ping.js diff --git a/backend/src/test/RandomUtils.js b/backend/src/test/RandomUtils.js new file mode 100644 index 00000000..da9e0e74 --- /dev/null +++ b/backend/src/test/RandomUtils.js @@ -0,0 +1,18 @@ +const randomstring = require("randomstring"); + +class RandomUtils { + static randomString(length) { + return randomstring.generate(length); + } + + static randomNumber(digits) { + digits = digits ? digits : 8; + return Math.floor(Math.random() * Math.pow(10, digits)); + } + + static randomEmail() { + return `${RandomUtils.randomString(6)}.${Date.now()}@${RandomUtils.randomString(6)}.com`; + } +} + +module.exports = RandomUtils; \ No newline at end of file diff --git a/backend/src/test/auth/ExpressAuthApis.js b/backend/src/test/auth/ExpressAuthApis.js new file mode 100644 index 00000000..55f733b6 --- /dev/null +++ b/backend/src/test/auth/ExpressAuthApis.js @@ -0,0 +1,121 @@ +const moment = require('moment'); +const fs = require('fs'); +const path = require('path'); +const jwt = require('jsonwebtoken'); + +function toBase64(value){ + return Buffer.from(value).toString('base64') +} + +class ExpressAuthApis { + constructor(app) { + this.app = app; + this.app.get('/authorize', this.auth.bind(this)); + this.app.get('/userinfo', this.userinfo.bind(this)); + this.app.get('/authcheck', this.authcheck.bind(this)); + this.app.get('/.well-known/jwks.json', this.wellKnown.bind(this)); + } + + wellKnown(req, res) { + console.log(req.query); + let jwks = fs.readFileSync(path.resolve(__dirname, '../config/express/certs/jwks.json')); + res.status(200).send(JSON.parse(jwks)); + } + + auth(req, res) { + let queryParams = req.query || {}; + let nonce = queryParams.nonce; + let state = queryParams.state; + let expiry = moment().add(100, 'd'); + let date = expiry.toDate(); + let host = req.get('host'); + + let urls = { + driver: this.extractUrl(queryParams, expiry, state, date, host, this._getDriver()), + admin: this.extractUrl(queryParams, expiry, state, date, host, this._getAdmin()), + falicitator: this.extractUrl(queryParams, expiry, state, date, host, this._getFacilitator()) + }; + + let redirectNow = queryParams.loginAs ? `window.location = "${urls[queryParams.loginAs]}"` : ''; + res.status(200).send(` + + +
+ + + `); + } + + extractUrl(queryParams, expiry, state, date, host, userInfo) { + let {accessToken, jwtToken} = this._authAs(queryParams, host, userInfo, expiry); + let url = `${queryParams.redirect_uri}#access_token=${accessToken}&id_token=${jwtToken}&refresh_token=6789&state=${state}&expires_in=${date.getTime()}`; + return url; + } + + _authAs(queryParams, host, userInfo, expiry) { + let payload = this._completeJWT(userInfo, host, expiry.toDate(), queryParams.nonce); + + let accessToken = userInfo.roles[0]; + let cert = fs.readFileSync(path.resolve(__dirname, '../config/express/certs/private.key')); + let jwtToken = jwt.sign(payload, cert, {algorithm: 'RS256'}); + return {accessToken, jwtToken}; + } + + _completeJWT(data, host, expiryDate, nonce){ + let result = { + "nonce": nonce.replace("@", "~"), + "iss": `https://${host}/`, + "aud": '1234', + "exp": expiryDate.getTime() / 1000, + "nbf": new Date().getTime() / 1000, + ...this._uriBased(data, `https://${host}/`) + }; + return result; + } + + _uriBased(data, uri){ + let result = {}; + Reflect.ownKeys(data).forEach(k => result[uri + k] = data[k]); + return result; + } + + _getDriver(uri){ + let data = require('../users/driver.json'); + return uri ? this._uriBased(data, uri) : data; + } + + _getAdmin(uri){ + let data = require('../users/admin.json'); + return uri ? this._uriBased(data, uri) : data; + } + + _getFacilitator(uri){ + let data = require('../users/facilitator.json'); + return uri ? this._uriBased(data, uri) : data; + } + + userinfo(req, res){ + let uri = req.get('origin') + '/'; + let tokens = (req.get('authorization') || '').split(' '); + switch (tokens[1]){ + case 'admin': + return res.status(200).send(this._getAdmin(uri)); + case 'facilitator': + return res.status(200).send(this._getFacilitator(uri)); + default: + return res.status(200).send(this._getDriver(uri)); + } + } + + authcheck(req, res){ + res.status(200).send({ name: 'Foo' }); + } + +} + +module.exports = ExpressAuthApis; \ No newline at end of file diff --git a/backend/src/test/config/express/certs/certificate.p12 b/backend/src/test/config/express/certs/certificate.p12 new file mode 100644 index 00000000..59e3a11f Binary files /dev/null and b/backend/src/test/config/express/certs/certificate.p12 differ diff --git a/backend/src/test/config/express/certs/certificate.pem b/backend/src/test/config/express/certs/certificate.pem new file mode 100644 index 00000000..8bd84ba7 --- /dev/null +++ b/backend/src/test/config/express/certs/certificate.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC9jCCAd4CCQDfAD2g+Y7RJzANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQGEwJh +dTEMMAoGA1UECAwDbnN3MQ8wDQYDVQQHDAZzeWRuZXkxDzANBgNVBAoMBmNhcnBh +bDAeFw0xODA4MjYwODI0NDBaFw0xOTA4MjYwODI0NDBaMD0xCzAJBgNVBAYTAmF1 +MQwwCgYDVQQIDANuc3cxDzANBgNVBAcMBnN5ZG5leTEPMA0GA1UECgwGY2FycGFs +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8HoBueljoBmBbe54tbu5 +ge0SFtebybklIKvmghgqR/Busi4azNwtGYBvvA0Ibodi/OGFJPvH2L89RuwWpxvp +Nb74NHEGlwN8QNq7jUSLrcgZ13zc9y4N030Xis4YNGO/dnsweGfqG/I3LjEl/ZJV +fDf5c+cZvFep7Z3OaPz9UawE5MkCA+GT5hycTLYhLNu4ODQfTJu3YmMHI5J0GyaF ++80Rj3vZ4XOwAgIlQk3uFVOrKswFvN/WM7HZTLwW6gX3SbG7LaCEE1toBoEtm3Nf +vKq2QaAe1AcjXWGgF7UEjzZ0B1kkp/LyV/fJkMxeQL5r3pz8tBIJzH478t3IuU22 +HQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBg5xdJx/0weyC5FXlc4i5cajs3ziEX +oGr704AgK3A+DNPkNPy5ZwfG7Axz0PSzrLWRAWhjnAGC4x+AcdGJtkoZ6/RfbkVO +s0cI7dYNLlGbBIwyiZnC36CtMYJ1blUBPZGF7hEdQER2Bq0yfgju9yr6YrpGwuUo +Kwnsl/nj03MnwbUc75nIPZRDLJmx7QsoTJm5R88l3MjCqcD6xSA9pX9wugnfKCNq +RRXV2ZRxiUsgAhcMEkyglYrawZ4gQi5Rp6/jNNjq0c5ARBYCjcZcBkk2buuKAZjv +AkXAAT/3E01s3af3+XXTEkfTj4/f84FloutSUVqXuLgNIYEztS9fHVho +-----END CERTIFICATE----- diff --git a/backend/src/test/config/express/certs/jwks.json b/backend/src/test/config/express/certs/jwks.json new file mode 100644 index 00000000..816546a5 --- /dev/null +++ b/backend/src/test/config/express/certs/jwks.json @@ -0,0 +1,17 @@ +{ + "keys": [ + { + "kty": "RSA", + "n": "wkRco_VjxfT_Zp2FCN-WnKTls9GaoRpUcrvwukH65u-Q0R9JUdjJiUQHxCgkBAOzEmsIeeEYRW19-E3Yk7fYzftbaD5cC9nZyf3jA9fF9kbbnD2wPFtk2Go9g5-TtWVKB4r_dd9D88tNAfTpa4b20uh9Uj92E6H0-fw7ZSg4kkkMv7mb0utRtsERi8-6hAu_kv3b7aayryBYVxKwwgkuXJ0SHJ3A9o1XL74i8l6vuJdcgdfSM8Sn9GYFbezuAR6JBbTdi55kSYf06rJqiUH-1Ep3yRv7MgWwWe8W_TRVYjGgbwJZxTdjE3Js-hU8q8mp6_cQtgjJ9wY2EjiliYPV_Vv9v1NfWFf-lodqe2eJCcfYKFDep3mUeV4pR_TJypbRWtlcnyuLNeSeL4ubpHZ8jWKWgHJ5iZd2O2-s16B_tEfRZ0cCEYNufPGbZzPGsw6YrhKJ-2kxAkrn7FJPK3NXMI3imEDQxCiEJl07DZm5Fg-cQsG4F7s_V8AG4s_BRTS6BBHtAkKpt9_G6nxWCOWMPdjTbXCHGdh2Ikruk4p-AlevYNuEbhOyGzSyHEc1Jgsu8vSOmffhzw9r5dWjIZ-fzWZTEh0PZDec1b-23Dv9fHnqjqtx3gorRg8cKv5u2KS_JfhbQq7luCZj0Yy8n6cTnYWbzb5m4-fNK-LDbukx0QE", + "e": "AQAB", + "d": "FKbdzlRM6gN0jz0ZomragQaKoMYckRnSdmwCWFqhSKGZHJId4kSQkDYaOUEnKwIVrFSSjxpd0Sulo5rq9jEqLCOXgQp4EI51N6L02ji7A8ZenEp2jMQOgW8x4Oc69aKx7wFYzPgSKj2njxixI8wyOxOaFv9GFyo1d8nXqpZPBUYcw9kAdrirdCksK1fG9TimggLYuXfqMPHYUlFbVxVQx2mvPjmLBOrbpOiqoGdueMomfGssHnK-hB3AYAI6_wV2_ZNPpgMNmD-KytRyvOq2I21TNJ7z4kp2h-kB4aMJnKm363Utg-iycKPOb_uH-p8yoDIgpaljhw4y-1boSikou1nVbLHpSuNtgnUsj4-JxvJHZorEqnizeJiFLECwYa6YVviPEySkIkfMCmfU1NekbHOmQmTCE_Uy5btgOFXCnsfdViU11mRUTQ1PCbVvDcPSMZSKHN2Fhh4u56ckuwLajzsOxYrjkOVEroZv5eW0fydg_zotEkcAyQtyAW90F7iVMcke8IvQPYeeiXjrXmIYvSulxD8Ze-nymQtr7Quru8WLzkUEdbLMS-776JYK4NevDbGqdKLSQbKi9ykU1xg8Cah4RI2A0gTKOZPES1epe0xPRJknvy2Uq8eUaDjBQnGqoAqMPeOWV8YuDF_aJ4UpURLCKttEazKNLdguNFqd1tE", + "p": "8b5rcAFydgFB0yZAy1ATxIaUpUp7GetdgAymQwzH7JnF6ebY0CpSqmVAZoTL-ZfU-FgvaS11b2wkyQFHcPUrtfmHP_Wuada_al_qc9FDvYeqnoNUGjzBN1HKnUBbXlpXv8nbbwI9NW65vC28U0J2w-g9XoDz7TgeD6ZzfkUAb2grV0CIM_qSPNMU0PqdTHcZ4nk6M_416cdU2Xw2iG_otJrVEg2EHRtqCc54LRFE9QCwb93VdH7ndj-hKlNjwkj_rL_R8Pxf09-Jm1Zh4IodjL2CVy7QxHODY1DjrDbcJ6O525Q7XeOA-MLm-HPwE3ltH94Q68b9mgMNqUHaN7EyXQ", + "q": "zbkwtg-jpHbi6ue3NS2WzjW7ILaOucK1f1GzAY-nZF1fCOK70lj0IewhYSh1VViu29TuabwM1YQoIagCdlmIcRAOrFOTfI6v6QsYDVoIzb4aUXXmx6VgbjKrf3hWePLsOGYZie4irfIezLBqKnx4Rwf11pUnG-yxptuk4AkH7v3C5Csl2QIRLMd7AOeNpKcUlr68xtCUJNNdHTE8EBmC79r5VEu2cBEPxlMtRY4mJVwtQOQgJOt3KeK_6LY3TY1o_TmOX4oyl4pbGPQ5ZO_vyBRuwKVeSEI83-QgEmmMKbsgFNSI2P36BBOvrmPTkyFv_-myxoaHW1WfeqcT1iE29Q", + "dp": "OUYbIDtHigu8M7GBcmnzdQFJoGuoBLkNuvPERGh3yWeZS1RlE9SjJzm7604VIXpGe9wwx2N8yjw97t19tpZvl7qZv56OhwbY7PPykSQIP5Qv6URGHb09LcUUEvOXciBHX-oMMh2-sLUeDiZr0vIRP1L7jzNQF2jPPnf6LvVcKAjvE8n3OxFnqj98VBK8R8yD9nMwRfc5gLy4LprONL8GzBtO1esb3OWM1uvy7wKDauSR6L8O_n2-ivaCUYvWO8adxKjhrY2tin4QlRv_Lnqqi5iuk8dEsPaJL3OPrVKGEGIq-4oAErt-5_ENVdnqDEMdhopPSf0oQl_s0agHcmBqBQ", + "dq": "tjsWSB-gYH4jUdwCMOv6Sx3Tbg3obeppJaf0PizHJaOEHHXj1FeRoj7t7oeNMBXHhtmeezDl2Xkgp3eQ_s8eirCjnsjFu68VagS2wJUBeWatH32l-TGoMtVVjyvExYzB7M-cTc8RDy6LU9vtn0b0sE7_2J30r8rCL0EfUokNawmESuNhulIXbqghcFKs6K5MEaBRzndS0zAbqlFDmCtRDjDVbXNH3wtuAssJLjTV24BEYdfyFWIzA3plbrmCUYMy_iSu1jivygb33SnxcVUM_RoEZUfXNYDfNaB5PHYcf5bPu0PtGPIZNuVNWNf6wbj74iBciXj_i8tT4qz_aE2xxQ", + "qi": "6R57N1Qyk2GUPUGJ0Yz50c9ooK5basDNXIKnzYGmmqaZRRF-cMywIwiR5PbfIyAd6MuW4tIoEvvv3VGW6NszpH-c6YhRCFBW4kvuv4lzdKNHpRP57MuFJiXlBe0KKso4G4vMaEnsx7ifNsWVjzNWgmNx06avpng7VTdyZSFbxe_JVRNUbkN5WJtZsh-IXF9L9LMsEE05r-VGnsjAz9ShD9-R-_TUk4Wsqzhm-ILo0NNCHfsW_qvC7kdysRa96tncMeQ45xuj_BuG2l6p8gdIjvGDErvfG4JhrdY3kyX7G_dISH5Do1U8mkIJQ2I-Z49MAN5BrBHmpeCSt8b1KUHzYg", + "alg": "RS256", + "use": "sig" + } + ] +} \ No newline at end of file diff --git a/backend/src/test/config/express/certs/key.pem b/backend/src/test/config/express/certs/key.pem new file mode 100644 index 00000000..ac797abe --- /dev/null +++ b/backend/src/test/config/express/certs/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDwegG56WOgGYFt +7ni1u7mB7RIW15vJuSUgq+aCGCpH8G6yLhrM3C0ZgG+8DQhuh2L84YUk+8fYvz1G +7BanG+k1vvg0cQaXA3xA2ruNRIutyBnXfNz3Lg3TfReKzhg0Y792ezB4Z+ob8jcu +MSX9klV8N/lz5xm8V6ntnc5o/P1RrATkyQID4ZPmHJxMtiEs27g4NB9Mm7diYwcj +knQbJoX7zRGPe9nhc7ACAiVCTe4VU6sqzAW839YzsdlMvBbqBfdJsbstoIQTW2gG +gS2bc1+8qrZBoB7UByNdYaAXtQSPNnQHWSSn8vJX98mQzF5AvmvenPy0EgnMfjvy +3ci5TbYdAgMBAAECggEAQzjX4rBrOQXoOGLBO4wOf1NWCyyaT/mBd3CWLyeyKgn7 +57Mbqsihks9kbJz4Dm2qLiacoYoAg4ZyCrFUY8JZnryThZVS0kQXJ8n9Q7A1m46k +Kqis3CvzkXsWaabS/VIk42nsUrw5pTZAVplGlWuimebLxKqFdzDKP7ItUQvnhDyX +JQ1eIfbdf/Xf+QosNDSMyVLC2z3hnyP/Q+ajRS034wKNmkth7GPIdwPbti5nbVHD +m+QzL/wI13dyp+3tnnNVuVGeelWcqiJ5+5v7wjyuRDmk/POp0ETvX1EYO3xm03Jw +XWwA/kvX13n2ILgHry1gNV/Nqe13+gQWSyUJBzgfwQKBgQD8PfmpR4loz1Xj1p5o +xahJL3MHRaE3LHtl8RNoeUSOILIat2LeFrIHRETnd7/XZ/3knLeexqAT6XD/bkfg +iDuZIazU/+k8QCZX3NXhUvl2ja7zENDQI9O3q5FNKtQpBwOh0rUa6+koJLtQSWBc +7SMp3G8ZITDujyrgb0SnZHRWZwKBgQD0Dyi8A9IG/0DL7ZENKbSmSdKWJ5xqydY2 +cFh9RRb/dFX7It8hBXp+HCRGJaJ3xVp1NFCO7KVdZ/xLO63RmQBqgAHiNe5oP26w +fzeaj644xABUM9GsFxDFSoNOgGB9aBjVHTctI9LtQYUrxVLYwfwTsFk4nz6JXDwZ +ysMa5mJU2wKBgQCo8QFkJWuc7TEDll0moyvhSIxsVHBzubE0R9DN6lrGLpPbXPQ0 +91Jpl4nm1ceBiD7+fRBmoXXZoEJ0cfJmKhhwqaNOTdBy8Cw+MMR3U9GNW7vPRHX2 +0egdXiXFX2gVyoLeQXfW/iZ9IozqaxrFYnZotSEb3aUeQnlfNGbxDlD1BQKBgQDG +w6nLJdlhNXUSIFKnw7WKUEkfXPc4yVSaVXjb3O7T8W0s8MOD4zBVkJnCP1hH7wSy +u354SCaNIekJZHs1XkRGeCYQkxes8yw0tgcU67taI9aYvPMgElDoPL0fq9HpkGPY +/Mg5DRn1fAz3Dudf/OxNJKwlPxvxRprz7nxjZgnjTQKBgDfW/bjsMGn/XFodVsqS +j3CcNLcIqRMw4fhiRTO6AfZL9wMhqglzt2uZuXibPz3+7aN/QeqRm1vWwjC+ckzK +UUHDvbMJqBJ/Wj9bpeVifjxML8yRkhWnxlCmxWywyFNHToBaI0/1FT+jEsYfd1bE +TX6ZfR8fcsygXYzco244LGmY +-----END PRIVATE KEY----- diff --git a/backend/src/test/config/express/certs/private.key b/backend/src/test/config/express/certs/private.key new file mode 100644 index 00000000..8ca6861a --- /dev/null +++ b/backend/src/test/config/express/certs/private.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKQIBAAKCAgEAwkRco/VjxfT/Zp2FCN+WnKTls9GaoRpUcrvwukH65u+Q0R9J +UdjJiUQHxCgkBAOzEmsIeeEYRW19+E3Yk7fYzftbaD5cC9nZyf3jA9fF9kbbnD2w +PFtk2Go9g5+TtWVKB4r/dd9D88tNAfTpa4b20uh9Uj92E6H0+fw7ZSg4kkkMv7mb +0utRtsERi8+6hAu/kv3b7aayryBYVxKwwgkuXJ0SHJ3A9o1XL74i8l6vuJdcgdfS +M8Sn9GYFbezuAR6JBbTdi55kSYf06rJqiUH+1Ep3yRv7MgWwWe8W/TRVYjGgbwJZ +xTdjE3Js+hU8q8mp6/cQtgjJ9wY2EjiliYPV/Vv9v1NfWFf+lodqe2eJCcfYKFDe +p3mUeV4pR/TJypbRWtlcnyuLNeSeL4ubpHZ8jWKWgHJ5iZd2O2+s16B/tEfRZ0cC +EYNufPGbZzPGsw6YrhKJ+2kxAkrn7FJPK3NXMI3imEDQxCiEJl07DZm5Fg+cQsG4 +F7s/V8AG4s/BRTS6BBHtAkKpt9/G6nxWCOWMPdjTbXCHGdh2Ikruk4p+AlevYNuE +bhOyGzSyHEc1Jgsu8vSOmffhzw9r5dWjIZ+fzWZTEh0PZDec1b+23Dv9fHnqjqtx +3gorRg8cKv5u2KS/JfhbQq7luCZj0Yy8n6cTnYWbzb5m4+fNK+LDbukx0QECAwEA +AQKCAgAUpt3OVEzqA3SPPRmiatqBBoqgxhyRGdJ2bAJYWqFIoZkckh3iRJCQNho5 +QScrAhWsVJKPGl3RK6Wjmur2MSosI5eBCngQjnU3ovTaOLsDxl6cSnaMxA6BbzHg +5zr1orHvAVjM+BIqPaePGLEjzDI7E5oW/0YXKjV3ydeqlk8FRhzD2QB2uKt0KSwr +V8b1OKaCAti5d+ow8dhSUVtXFVDHaa8+OYsE6tuk6KqgZ254yiZ8aywecr6EHcBg +Ajr/BXb9k0+mAw2YP4rK1HK86rYjbVM0nvPiSnaH6QHhowmcqbfrdS2D6LJwo85v ++4f6nzKgMiClqWOHDjL7VuhKKSi7WdVsselK422CdSyPj4nG8kdmisSqeLN4mIUs +QLBhrphW+I8TJKQiR8wKZ9TU16Rsc6ZCZMIT9TLlu2A4VcKex91WJTXWZFRNDU8J +tW8Nw9IxlIoc3YWGHi7npyS7AtqPOw7FiuOQ5USuhm/l5bR/J2D/Oi0SRwDJC3IB +b3QXuJUxyR7wi9A9h56JeOteYhi9K6XEPxl76fKZC2vtC6u7xYvORQR1ssxL7vvo +lgrg168Nsap0otJBsqL3KRTXGDwJqHhEjYDSBMo5k8RLV6l7TE9EmSe/LZSrx5Ro +OMFCcaqgCow945ZXxi4MX9onhSlREsIq20RrMo0t2C40Wp3W0QKCAQEA8b5rcAFy +dgFB0yZAy1ATxIaUpUp7GetdgAymQwzH7JnF6ebY0CpSqmVAZoTL+ZfU+FgvaS11 +b2wkyQFHcPUrtfmHP/Wuada/al/qc9FDvYeqnoNUGjzBN1HKnUBbXlpXv8nbbwI9 +NW65vC28U0J2w+g9XoDz7TgeD6ZzfkUAb2grV0CIM/qSPNMU0PqdTHcZ4nk6M/41 +6cdU2Xw2iG/otJrVEg2EHRtqCc54LRFE9QCwb93VdH7ndj+hKlNjwkj/rL/R8Pxf +09+Jm1Zh4IodjL2CVy7QxHODY1DjrDbcJ6O525Q7XeOA+MLm+HPwE3ltH94Q68b9 +mgMNqUHaN7EyXQKCAQEAzbkwtg+jpHbi6ue3NS2WzjW7ILaOucK1f1GzAY+nZF1f +COK70lj0IewhYSh1VViu29TuabwM1YQoIagCdlmIcRAOrFOTfI6v6QsYDVoIzb4a +UXXmx6VgbjKrf3hWePLsOGYZie4irfIezLBqKnx4Rwf11pUnG+yxptuk4AkH7v3C +5Csl2QIRLMd7AOeNpKcUlr68xtCUJNNdHTE8EBmC79r5VEu2cBEPxlMtRY4mJVwt +QOQgJOt3KeK/6LY3TY1o/TmOX4oyl4pbGPQ5ZO/vyBRuwKVeSEI83+QgEmmMKbsg +FNSI2P36BBOvrmPTkyFv/+myxoaHW1WfeqcT1iE29QKCAQA5RhsgO0eKC7wzsYFy +afN1AUmga6gEuQ2688REaHfJZ5lLVGUT1KMnObvrThUhekZ73DDHY3zKPD3u3X22 +lm+Xupm/no6HBtjs8/KRJAg/lC/pREYdvT0txRQS85dyIEdf6gwyHb6wtR4OJmvS +8hE/UvuPM1AXaM8+d/ou9VwoCO8Tyfc7EWeqP3xUErxHzIP2czBF9zmAvLgums40 +vwbMG07V6xvc5YzW6/LvAoNq5JHovw7+fb6K9oJRi9Y7xp3EqOGtja2KfhCVG/8u +eqqLmK6Tx0Sw9okvc4+tUoYQYir7igASu37n8Q1V2eoMQx2Gik9J/ShCX+zRqAdy +YGoFAoIBAQC2OxZIH6BgfiNR3AIw6/pLHdNuDeht6mklp/Q+LMclo4QcdePUV5Gi +Pu3uh40wFceG2Z57MOXZeSCnd5D+zx6KsKOeyMW7rxVqBLbAlQF5Zq0ffaX5Magy +1VWPK8TFjMHsz5xNzxEPLotT2+2fRvSwTv/YnfSvysIvQR9SiQ1rCYRK42G6Uhdu +qCFwUqzorkwRoFHOd1LTMBuqUUOYK1EOMNVtc0ffC24CywkuNNXbgERh1/IVYjMD +emVuuYJRgzL+JK7WOK/KBvfdKfFxVQz9GgRlR9c1gN81oHk8dhx/ls+7Q+0Y8hk2 +5U1Y1/rBuPviIFyJeP+Ly1PirP9oTbHFAoIBAQDpHns3VDKTYZQ9QYnRjPnRz2ig +rltqwM1cgqfNgaaapplFEX5wzLAjCJHk9t8jIB3oy5bi0igS++/dUZbo2zOkf5zp +iFEIUFbiS+6/iXN0o0elE/nsy4UmJeUF7Qoqyjgbi8xoSezHuJ82xZWPM1aCY3HT +pq+meDtVN3JlIVvF78lVE1RuQ3lYm1myH4hcX0v0sywQTTmv5UaeyMDP1KEP35H7 +9NSThayrOGb4gujQ00Id+xb+q8LuR3KxFr3q2dwx5DjnG6P8G4baXqnyB0iO8YMS +u98bgmGt1jeTJfsb90hIfkOjVTyaQglDYj5nj0wA3kGsEeal4JK3xvUpQfNi +-----END RSA PRIVATE KEY----- diff --git a/backend/src/test/config/express/certs/public.key b/backend/src/test/config/express/certs/public.key new file mode 100644 index 00000000..84babe0a --- /dev/null +++ b/backend/src/test/config/express/certs/public.key @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDCRFyj9WPF9P9mnYUI35acpOWz0ZqhGlRyu/C6Qfrm75DRH0lR2MmJRAfEKCQEA7MSawh54RhFbX34TdiTt9jN+1toPlwL2dnJ/eMD18X2RtucPbA8W2TYaj2Dn5O1ZUoHiv9130Pzy00B9OlrhvbS6H1SP3YTofT5/DtlKDiSSQy/uZvS61G2wRGLz7qEC7+S/dvtprKvIFhXErDCCS5cnRIcncD2jVcvviLyXq+4l1yB19IzxKf0ZgVt7O4BHokFtN2LnmRJh/TqsmqJQf7USnfJG/syBbBZ7xb9NFViMaBvAlnFN2MTcmz6FTyryanr9xC2CMn3BjYSOKWJg9X9W/2/U19YV/6Wh2p7Z4kJx9goUN6neZR5XilH9MnKltFa2VyfK4s15J4vi5ukdnyNYpaAcnmJl3Y7b6zXoH+0R9FnRwIRg2588ZtnM8azDpiuEon7aTECSufsUk8rc1cwjeKYQNDEKIQmXTsNmbkWD5xCwbgXuz9XwAbiz8FFNLoEEe0CQqm338bqfFYI5Yw92NNtcIcZ2HYiSu6Tin4CV69g24RuE7IbNLIcRzUmCy7y9I6Z9+HPD2vl1aMhn5/NZlMSHQ9kN5zVv7bcO/18eeqOq3HeCitGDxwq/m7YpL8l+FtCruW4JmPRjLyfpxOdhZvNvmbj580r4sNu6THRAQ== test@carpal.com diff --git a/backend/src/test/expressApis.js b/backend/src/test/expressApis.js new file mode 100644 index 00000000..47570deb --- /dev/null +++ b/backend/src/test/expressApis.js @@ -0,0 +1,58 @@ +const express = require('express'); +const fs = require('fs'); +const path = require('path'); +const CreateRideService = require('../main/rides/CreateRideService'); +const ListRidesService = require('../main/rides/ListRidesService'); +const FindOneRideService = require('../main/rides/FindOneRideService'); +const UpdateRideService = require('../main/rides/UpdateRideService'); +const DatabaseManager = require('../main/database/DatabaseManager'); +const ExpressRideApis = require('./rides/express/ExpressRidesApis'); +const ExpressAuthApis = require('./auth/ExpressAuthApis'); +const AwsLambdaRideApis = require('../main/rides/aws/AwsLambdaRideApis'); +const bodyParser = require('body-parser'); +const databaseManager = new DatabaseManager(); + +const https = require('https'); +const http = require('http'); + +process.on('uncaughtException', function (err) { + console.log(err); +}); + +process.env.DOMAIN = 'localhost:8081'; + +const createRideService = new CreateRideService(databaseManager); +const listRidesService = new ListRidesService(databaseManager); +const findOneRideService = new FindOneRideService(databaseManager); +const updateRideService = new UpdateRideService(databaseManager); +const app = express(); +app.use(bodyParser.json()); // for parsing application/json +app.use(bodyParser.urlencoded({extended: true})); // for parsing application/x-www-form-urlencoded + +app.use(function (req, res, next) { + res.header("Access-Control-Allow-Origin", req.get("Origin")); + res.header("Access-Control-Allow-Headers", "*"); + res.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE"); + next(); +}); + +new ExpressAuthApis(app); +let awsLambdaRideApis = new AwsLambdaRideApis(createRideService, + listRidesService, + findOneRideService, + updateRideService); + +new ExpressRideApis(app, awsLambdaRideApis); + +const options = { + key: fs.readFileSync(path.resolve(__dirname, './config/express/certs/key.pem')), + cert: fs.readFileSync(path.resolve(__dirname, './config/express/certs/certificate.pem')) +}; + +http.createServer(app).listen(8080, () => { + console.log("HTTP Server started and listening on port 8080") +}); + +https.createServer(options, app).listen(8081, () => { + console.log("HTTPS Server started and listening on port 8081") +}); \ No newline at end of file diff --git a/backend/src/test/rides/CreateRide.integration.test.js b/backend/src/test/rides/CreateRide.integration.test.js new file mode 100644 index 00000000..6784f5d2 --- /dev/null +++ b/backend/src/test/rides/CreateRide.integration.test.js @@ -0,0 +1,86 @@ +const DatabaseManager = require('../../main/database/DatabaseManager'); +const RideStatus = require('../../main/rides/RideStatus'); +const CreateRideService = require('../../main/rides/CreateRideService'); +const FindRideTestRepository = require('./RideTestRepository'); +const RandomUtils = require('../RandomUtils'); +const moment = require('moment'); +const chai = require('chai'); +const chaiExclude = require('chai-exclude'); +const assert = chai.assert; +chai.use(chaiExclude); + +let createRideService; +let rideRequest; +let databaseManager; +let mockDatabaseManager; +let findRideTestRepository; +let connection; +let loginData; +let pickupTimeAndDateInUTC; + +before(async () => { + databaseManager = new DatabaseManager(); + connection = databaseManager.createConnection() ; + mockDatabaseManager = new DatabaseManager(); + mockDatabaseManager.createConnection = () => connection; + mockDatabaseManager.closeConnection = () => Promise.resolve(); +}); + +after(async () => { + await databaseManager.closeConnection(connection); +}); + +beforeEach(async () => { + await databaseManager.beginTransaction(connection); + + createRideService = new CreateRideService(mockDatabaseManager); + findRideTestRepository = new FindRideTestRepository(databaseManager); +}); + +afterEach(async () => { + await databaseManager.rollback(connection); +}); + +beforeEach(function setupData() { + loginData = {email: RandomUtils.randomEmail()}; + pickupTimeAndDateInUTC = moment(); + const clientEmail = `client.test.${Date.now()}@carpal.com`; + rideRequest = { + 'carType': 'suv', + 'client': clientEmail, + 'deleted': 0, + 'description': 'wanna surf', + 'driverGender': 'male', + 'fbLink': 'http://facebook.com/profile/facilitator.test.1', + 'locationFrom': { + "latitude": 1234, + "longitude": 4567, + "placeName": "home", + "suburb": "Bondi Junction", + "postcode": "2010" + }, + 'locationTo': { + "latitude": 9999, + "longitude": 7777, + "placeName": "beach", + "suburb": "Bondi", + "postcode": "2011" + }, + 'pickupTimeAndDateInUTC': pickupTimeAndDateInUTC.format('YYYY-MM-DD HH:mm:ss.SSS'), + 'status': RideStatus.OPEN + }; +}); + +describe('SQL', function () { + it('should insert and retrieve ride', async function () { + await createRideService.createRide(rideRequest, loginData); + let storedRide = await findRideTestRepository.findOneByClientEmail(rideRequest.client, connection); + + assert.deepEqualExcluding(storedRide, rideRequest, ['id', 'datetime', 'pickupTimeAndDateInUTC', 'facilitatorId']); + assert.equal(storedRide.facilitatorId, loginData.email); + assert.equal(storedRide.pickupTimeAndDateInUTC.setMilliseconds(0), pickupTimeAndDateInUTC.toDate().setMilliseconds(0)); + }); +}); + + + diff --git a/backend/src/test/rides/FindOneRide.integration.test.js b/backend/src/test/rides/FindOneRide.integration.test.js new file mode 100644 index 00000000..2a6f57a0 --- /dev/null +++ b/backend/src/test/rides/FindOneRide.integration.test.js @@ -0,0 +1,112 @@ +const DatabaseManager = require('../../main/database/DatabaseManager'); +const FindOneRideService = require('../../main/rides/FindOneRideService'); +const RideTestRepository = require('./RideTestRepository'); +const RideEntityBuilder = require('./RideEntityBuilder'); +const RideRepository = require('../../main/rides/RideRepository'); +const RandomUtils = require('../RandomUtils'); + +let findOneRideService; +let databaseManager; +let mockDatabaseManager; +let rideRepository; +let connection; +let rideTestRepository; + +const chai = require('chai'); +const chaiExclude = require('chai-exclude'); +const assert = chai.assert; +chai.use(chaiExclude); + +before(async () => { + databaseManager = new DatabaseManager(); + mockDatabaseManager = new DatabaseManager(); + connection = databaseManager.createConnection(); + mockDatabaseManager.createConnection = () => connection; + mockDatabaseManager.closeConnection = () => Promise.resolve(null); +}); + +after(async () => { + await databaseManager.closeConnection(connection); +}); + +beforeEach(async () => { + await databaseManager.beginTransaction(connection); + + findOneRideService = new FindOneRideService(mockDatabaseManager); + rideTestRepository = new RideTestRepository(mockDatabaseManager); + rideRepository = new RideRepository(databaseManager); +}); + +afterEach(async () => { + await databaseManager.rollback(connection); +}); + +describe('When find one ride', async () => { + it('should show single ride that was created by facilitator', async function () { + // given + const loginData = {email: RandomUtils.randomEmail(), role: 'facilitator'}; + const email = loginData.email; + const ride = randomRideWithFacilitator(email); + const rideEntity = await databaseContainsRide(ride); + + // when + const storedRide = await findOneRideService.findOne(rideEntity.id, loginData); + + // then + assert.deepEqualExcluding(ride, storedRide, 'id'); + }); + + it('should NOT show ride that was created by other facilitator', async function () { + // given + const loginData = {email: RandomUtils.randomEmail(), role: 'facilitator'}; + const ride = randomRideWithFacilitator(RandomUtils.randomEmail()); + const rideEntity = await databaseContainsRide(ride); + + // when + const storedRide = await findOneRideService.findOne(rideEntity.id, loginData); + + // then + assert.isNull(storedRide); + }); + + it('should show single ride when user is admin', async function () { + // given + const loginData = {email: RandomUtils.randomEmail(), role: 'admin'}; + const ride = randomRideWithFacilitator(RandomUtils.randomEmail()); + const rideEntity = await databaseContainsRide(ride); + + // when + const storedRide = await findOneRideService.findOne(rideEntity.id, loginData); + + // then + assert.deepEqualExcluding(ride, storedRide, 'id'); + }); + + it('should not show single ride when user is driver', async function () { + // given + const loginData = {email: RandomUtils.randomEmail(), role: 'driver'}; + const ride = randomRideWithFacilitator(RandomUtils.randomEmail()); + const rideEntity = await databaseContainsRide(ride); + + // when + const storedRide = await findOneRideService.findOne(rideEntity.id, loginData); + + // then + assert.isNull(storedRide); + }); + + async function databaseContainsRide(ride) { + await rideRepository.create(ride, connection); + let rideEntity = rideTestRepository.findOneByClientEmail(ride.client); + return rideEntity; + } + + function randomRideWithFacilitator(facilitatorId) { + const ride = RideEntityBuilder.randomRideEntity(); + ride.facilitatorId = facilitatorId; + return ride; + } +}); + + + diff --git a/backend/src/test/rides/ListRides.integration.test.js b/backend/src/test/rides/ListRides.integration.test.js new file mode 100644 index 00000000..373b90df --- /dev/null +++ b/backend/src/test/rides/ListRides.integration.test.js @@ -0,0 +1,115 @@ +const DatabaseManager = require('../../main/database/DatabaseManager'); +const ListRidesController = require('../../main/rides/ListRidesService'); +const FindRideTestRepository = require('./RideTestRepository'); +const RideEntityBuilder = require('./RideEntityBuilder'); +const RideRepository = require('../../main/rides/RideRepository'); +const RandomUtils = require('../RandomUtils'); + +let listRideController; +let databaseManager; +let mockDatabaseManager; +let findRideTestRepository; +let rideRepository; +let connection; + +const chai = require('chai'); +const chaiExclude = require('chai-exclude'); +const assert = chai.assert; +chai.use(chaiExclude); + +before(async () => { + databaseManager = new DatabaseManager(); + mockDatabaseManager = new DatabaseManager(); + connection = databaseManager.createConnection(); + mockDatabaseManager.createConnection = () => connection; + mockDatabaseManager.closeConnection = () => Promise.resolve(null); +}); + +after(async () => { + await databaseManager.closeConnection(connection); +}); + +beforeEach(async () => { + await databaseManager.beginTransaction(connection); + + listRideController = new ListRidesController(mockDatabaseManager); + findRideTestRepository = new FindRideTestRepository(databaseManager); + rideRepository = new RideRepository(databaseManager); +}); + +afterEach(async () => { + await databaseManager.rollback(connection); +}); + +describe('When listing rides', async () => { + it('should show rides for facilitator', async function () { + // given + const loginData = {email: RandomUtils.randomEmail(), roles: ['facilitator']}; + const email = loginData.email; + const ride1 = randomRideWithFacilitator(email); + const ride2 = randomRideWithFacilitator(email); + await databaseContainsRides(ride1, ride2); + + // when + const rides = await listRideController.listRides({listType: 'facilitator'}, loginData); + + assert.deepEqualExcluding(rides, [ride1, ride2], 'id'); + }); + + it('should show all rides for admin', async function () { + // given + const loginData = {email: RandomUtils.randomEmail(), roles: ['admin']}; + const ride1 = RideEntityBuilder.randomRideEntity(); + const ride2 = RideEntityBuilder.randomRideEntity(); + await databaseContainsRides(ride1, ride2); + + // when + const rides = (await listRideController.listRides({}, loginData)).map(removeId); + + assert.deepInclude(rides, ride1); + assert.deepInclude(rides, ride2); + }); + + it('should show all rides for driver', async function () { + // given + const loginData = {email: RandomUtils.randomEmail(), roles: ['driver'], driverGender: 'male'}; + const ride1 = randomRideWithGender('male'); + const ride2 = randomRideWithGender('female'); + const ride3 = randomRideWithGender('any'); + await databaseContainsRides(ride1, ride2, ride3); + + // when + const rides = (await listRideController.listRides({}, loginData)).map(removeId); + + assert.deepInclude(rides, ride1); + assert.deepInclude(rides, ride3); + assert.notDeepInclude(rides, ride2); + // assert.deepEqualExcluding(rides, [ride1, ride3], 'id'); + }); + + async function databaseContainsRides(...rides) { + for (let ride of rides) { + await rideRepository.create(ride, connection); + } + } + + function removeId(ride){ + delete ride.id; + return ride; + } + + function randomRideWithGender(gender) { + const ride = RideEntityBuilder.randomRideEntity(); + ride.driverGender = gender; + return ride; + } + + function randomRideWithFacilitator(facilitatorId) { + const ride = RideEntityBuilder.randomRideEntity(); + ride.facilitatorId = facilitatorId; + return ride; + } +}); + + + diff --git a/backend/src/test/rides/RideEntityBuilder.js b/backend/src/test/rides/RideEntityBuilder.js new file mode 100644 index 00000000..974f41e5 --- /dev/null +++ b/backend/src/test/rides/RideEntityBuilder.js @@ -0,0 +1,38 @@ +const RandomUtils = require('../RandomUtils'); +const RideStatus = require('../../main/rides/RideStatus'); + +const now = new Date(); + +class RideEntityBuilder { + static randomRideEntity() { + const client = `client.${Date.now()}`; + const facilitator = `facilitator.${Date.now()}`; + return { + carType: "suv", + client: `${client}@${RandomUtils.randomString(5)}.com`, + deleted: 0, + driverGender: (Math.random() * 1000) % 2 ? "male" : "female", + facilitatorId: `${facilitator}@${RandomUtils.randomString(5)}.com`, + fbLink: `http://facebook.com/profile/${client}`, + locationFrom: { + latitude: RandomUtils.randomNumber(4), + longitude: RandomUtils.randomNumber(4), + placeName: RandomUtils.randomString(10), + postcode: RandomUtils.randomNumber(4).toString(), + suburb: RandomUtils.randomString(10) + }, + locationTo: { + latitude: RandomUtils.randomNumber(4), + longitude: RandomUtils.randomNumber(4), + placeName: RandomUtils.randomString(10), + postcode: RandomUtils.randomNumber(4).toString(), + suburb: RandomUtils.randomString(10) + }, + pickupTimeAndDateInUTC: new Date(new Date(now.setMilliseconds(0)).setDate(now.getDate() + 5)), + status: RideStatus.OPEN, + description: RandomUtils.randomString(10), + } + } +} + +module.exports = RideEntityBuilder; \ No newline at end of file diff --git a/backend/src/test/rides/RideMapper.unit.test.js b/backend/src/test/rides/RideMapper.unit.test.js new file mode 100644 index 00000000..e78ad4d5 --- /dev/null +++ b/backend/src/test/rides/RideMapper.unit.test.js @@ -0,0 +1,110 @@ +const RideMapper = require('../../main/rides/RideMapper'); +const RandomUtils = require('../RandomUtils'); +const RideStatus = require('../../main/rides/RideStatus'); +const RideEntityBuilder = require('./RideEntityBuilder'); + +const chai = require('chai'); +const chaiExclude = require('chai-exclude'); +const assert = chai.assert; +const moment = require('moment'); +chai.use(chaiExclude); + + +describe('RideMapper', async () => { + it('should convert dto to entity', async function () { + const pickupTimeAndDateInUTC = moment(); + const facilitatorId = RandomUtils.randomEmail(); + const dto = { + 'carType': 'suv', + 'client': RandomUtils.randomEmail(), + 'deleted': 0, + 'description': RandomUtils.randomString(10), + 'driverGender': (RandomUtils.randomNumber(2) % 2) ? 'male' : 'female', + 'fbLink': 'http://facebook.com/profile/' + RandomUtils.randomString(10), + 'locationFrom': { + "latitude": RandomUtils.randomNumber(4), + "longitude": RandomUtils.randomNumber(4), + "placeName": RandomUtils.randomString(10), + "suburb": RandomUtils.randomString(10), + "postcode": RandomUtils.randomNumber(4) + }, + 'locationTo': { + "latitude": RandomUtils.randomNumber(4), + "longitude": RandomUtils.randomNumber(4), + "placeName": RandomUtils.randomString(10), + "suburb": RandomUtils.randomString(10), + "postcode": RandomUtils.randomNumber(4) + }, + 'pickupTimeAndDateInUTC': pickupTimeAndDateInUTC.format('YYYY-MM-DD HH:mm:ss.SSS'), + 'status': RideStatus.OPEN + }; + + const entity = RideMapper.dtoToEntity(dto, facilitatorId); + + assert.deepEqual(entity, { + client: dto.client, + pickupTimeAndDateInUTC: new Date(dto.pickupTimeAndDateInUTC), + locationFrom: { + latitude: dto.locationFrom.latitude, + longitude: dto.locationFrom.longitude, + suburb: dto.locationFrom.suburb, + placeName: dto.locationFrom.placeName, + postcode: dto.locationFrom.postcode, + }, + locationTo: { + latitude: dto.locationTo.latitude, + longitude: dto.locationTo.longitude, + suburb: dto.locationTo.suburb, + placeName: dto.locationTo.placeName, + postcode: dto.locationTo.postcode, + }, + fbLink: dto.fbLink, + driverGender: dto.driverGender, + carType: dto.carType, + status: dto.status, + deleted: 0, + facilitatorId: facilitatorId, + description: dto.description + }); + }); + + it('should convert entity to dto', async function () { + // given + const entity = RideEntityBuilder.randomRideEntity(); + + // then + const dto = RideMapper.entityToDto(entity); + + // then + assert.deepEqual(dto, { + client: entity.client, + pickupTimeAndDateInUTC: new Date(entity.pickupTimeAndDateInUTC), + locationFrom: { + latitude: entity.locationFrom.x, + longitude: entity.locationFrom.y, + suburb: entity.suburbFrom, + postcode: entity.postCodeFrom, + placeName: entity.placeNameFrom + }, + locationTo: { + latitude: entity.locationTo.x, + longitude: entity.locationTo.y, + suburb: entity.suburbTo, + postcode: entity.postCodeTo, + placeName: entity.placeNameTo + }, + fbLink: entity.fbLink, + driverGender: entity.driverGender, + carType: entity.carType, + status: entity.status, + deleted: entity.deleted, + facilitatorId: entity.facilitatorId, + description: entity.description, + id: entity.id + }); + }); + +}); + + + diff --git a/backend/src/test/rides/RideTestRepository.js b/backend/src/test/rides/RideTestRepository.js new file mode 100644 index 00000000..c8705d3b --- /dev/null +++ b/backend/src/test/rides/RideTestRepository.js @@ -0,0 +1,15 @@ +const RidesMapper = require('../../main/rides/RideMapper'); + +class RideTestRepository { + constructor(databaseManager) { + this._databaseManager = databaseManager; + } + + findOneByClientEmail(email, connection){ + return this._databaseManager.query(`SELECT * FROM rides WHERE client = '${email}'`, connection) + .then(result => result instanceof Array && RidesMapper.entityToDto(result[0])); + } +} + + +module.exports = RideTestRepository; diff --git a/backend/src/test/rides/UpdateRide.integration.test.js b/backend/src/test/rides/UpdateRide.integration.test.js new file mode 100644 index 00000000..d63a07db --- /dev/null +++ b/backend/src/test/rides/UpdateRide.integration.test.js @@ -0,0 +1,110 @@ +const DatabaseManager = require('../../main/database/DatabaseManager'); +const RideStatus = require('../../main/rides/RideStatus'); +const UpdateRideService = require('../../main/rides/UpdateRideService'); +const FindRideTestRepository = require('./RideTestRepository'); +const RideEntityBuilder = require('./RideEntityBuilder'); +const RideRepository = require('../../main/rides/RideRepository'); +const RandomUtils = require('../RandomUtils'); +const RideMapper = require('../../main/rides/RideMapper'); +const moment = require('moment'); +const chai = require('chai'); +const chaiExclude = require('chai-exclude'); +const assert = chai.assert; +chai.use(chaiExclude); + +let updateRideService; +let databaseManager; +let mockDatabaseManager; +let findRideTestRepository; +let connection; +let loginData; +let pickupTimeAndDateInUTC; +let rideRepository; + +before(async () => { + databaseManager = new DatabaseManager(); + mockDatabaseManager = new DatabaseManager(); + rideRepository = new RideRepository(databaseManager); + connection = databaseManager.createConnection(); + mockDatabaseManager.createConnection = () => connection; + mockDatabaseManager.closeConnection = () => Promise.resolve(); +}); + +after(async () => { + await databaseManager.closeConnection(connection); +}); + +beforeEach(async () => { + await databaseManager.beginTransaction(connection); + + updateRideService = new UpdateRideService(mockDatabaseManager); + findRideTestRepository = new FindRideTestRepository(databaseManager); +}); + +afterEach(async () => { + await databaseManager.rollback(connection); +}); + +beforeEach(function setupData() { + loginData = {email: RandomUtils.randomEmail()}; + pickupTimeAndDateInUTC = moment(); +}); + +describe('SQL', function () { + it('should update and retrieve ride', async function () { + // given + const ride = randomRideWithFacilitator(loginData.email); + await databaseContainsRide(ride); + const modifiedRide = modifyRide(ride); + let storedRide = await findRideTestRepository.findOneByClientEmail(ride.client, connection); + + // when + await updateRideService.updateRide(storedRide.id, modifiedRide, loginData); + let modifiedRideFromDb = await findRideTestRepository.findOneByClientEmail(storedRide.client, connection); + + // then + assert.deepEqualExcluding(modifiedRideFromDb, RideMapper.dtoToEntity(modifiedRide), ['id', 'datetime', 'facilitatorId', 'pickupTimeAndDateInUTC']); + assert.equal(modifiedRideFromDb.facilitatorId, loginData.email); + assert.equal(modifiedRideFromDb.pickupTimeAndDateInUTC.setMilliseconds(0), pickupTimeAndDateInUTC.toDate().setMilliseconds(0)); + }); +}); + +function modifyRide(ride) { + return { + 'carType': 'suv', + 'client': ride.client, + 'deleted': 0, + 'description': 'wanna surf', + 'driverGender': 'male', + 'fbLink': 'http://facebook.com/profile/facilitator.test.1', + 'locationFrom': { + "latitude": 1234, + "longitude": 4567, + "placeName": "home", + "suburb": "Bondi Junction", + "postcode": "2010" + }, + 'locationTo': { + "latitude": 9999, + "longitude": 7777, + "placeName": "beach", + "suburb": "Bondi", + "postcode": "2011" + }, + 'pickupTimeAndDateInUTC': pickupTimeAndDateInUTC.format('YYYY-MM-DD HH:mm:ss.SSS'), + 'status': RideStatus.OPEN + }; +} + +async function databaseContainsRide(ride) { + await rideRepository.create(ride, connection); +} + +function randomRideWithFacilitator(facilitatorId) { + const ride = RideEntityBuilder.randomRideEntity(); + ride.facilitatorId = facilitatorId; + return ride; +} + + + diff --git a/backend/src/test/rides/express/ExpressRidesApis.js b/backend/src/test/rides/express/ExpressRidesApis.js new file mode 100644 index 00000000..01879698 --- /dev/null +++ b/backend/src/test/rides/express/ExpressRidesApis.js @@ -0,0 +1,60 @@ +class ExpressRideApis { + constructor(app, awsLambdaRideApis) { + this.app = app; + this.awsLambdaRideApis = awsLambdaRideApis; + this.app.post('/rides', this.create.bind(this)); + this.app.put('/rides/:id', this.update.bind(this)); + this.app.get('/rides/:id', this.findOne.bind(this)); + this.app.get('/rides', this.list.bind(this)); + } + + create(req, res) { + this.awsLambdaRideApis.create(this._extractAwsEvent(req), {}, (error, result) => { + if (error) { + return res.status(500).send(error); + } + res.status(200).send(result); + }); + } + + update(req, res) { + this.awsLambdaRideApis.update(this._extractAwsEvent(req), {}, (error, result) => { + if (error) { + return res.status(500).send(error); + } + res.status(200).send(result); + }); + } + + list(req, res) { + this.awsLambdaRideApis.list(this._extractAwsEvent(req), {}, (error, result) => { + if (error) { + return res.status(500).send(error); + } + res.status(200).send(result); + }); + } + + findOne(req, res) { + this.awsLambdaRideApis.findOne(this._extractAwsEvent(req), {}, (error, result) => { + if (error) { + return res.status(500).send(error); + } + res.status(200).send(result); + }); + } + + _extractAwsEvent(req) { + let event = { + headers: { + Authorization: req.get('authorization') + }, + body: JSON.stringify(req.body), + pathParameters: req.params || {}, + queryStringParameters: req.query || {}, + }; + return event; + } +} + +module.exports = ExpressRideApis; \ No newline at end of file diff --git a/backend/src/test/users/admin.json b/backend/src/test/users/admin.json new file mode 100644 index 00000000..2940aa80 --- /dev/null +++ b/backend/src/test/users/admin.json @@ -0,0 +1,5 @@ +{ + "email": "test-admin@carpal.com", + "gender": "male", + "roles": ["admin"] +} \ No newline at end of file diff --git a/backend/src/test/users/driver.json b/backend/src/test/users/driver.json new file mode 100644 index 00000000..37b3f25b --- /dev/null +++ b/backend/src/test/users/driver.json @@ -0,0 +1,6 @@ +{ + "email": "test-driver@carpal.com", + "gender": "male", + "car": "suv", + "roles": ["driver"] +} \ No newline at end of file diff --git a/backend/src/test/users/facilitator.json b/backend/src/test/users/facilitator.json new file mode 100644 index 00000000..c861cba6 --- /dev/null +++ b/backend/src/test/users/facilitator.json @@ -0,0 +1,5 @@ +{ + "email": "test-facilitator@carpal.com", + "gender": "female", + "roles": ["facilitator"] +} \ No newline at end of file diff --git a/infrastructure/services/writeSecrets.sh b/backend/writeSecrets.sh similarity index 100% rename from infrastructure/services/writeSecrets.sh rename to backend/writeSecrets.sh diff --git a/doc/carpal-api.yaml b/doc/carpal-api.yaml index d1d69ae9..3ed95ba0 100644 --- a/doc/carpal-api.yaml +++ b/doc/carpal-api.yaml @@ -162,7 +162,7 @@ components: type: string status: type: string - description: Can be Open / Closed + description: Can be Open / Confirmed / Canceled / Ended Location: properties: latitude: diff --git a/doc/sampledata.json b/doc/sampledata.json index 7140af8b..17d20e24 100644 --- a/doc/sampledata.json +++ b/doc/sampledata.json @@ -65,7 +65,7 @@ "fbLink": "www.facebook.com/events/965341583633198", "driverGender": "Female", "carType": "SUV", - "status": "Closed", + "status": "Ended", "facilitatorId": 1 }, { @@ -111,7 +111,7 @@ "fbLink": "www.facebook.com/events/965341583633198", "driverGender": "All", "carType": "SUV", - "status": "Closed", + "status": "Ended", "facilitatorId": 2 }, { @@ -180,7 +180,7 @@ "fbLink": "www.facebook.com/events/965341583633198", "driverGender": "All", "carType": "Normal", - "status": "Closed", + "status": "Ended", "facilitatorId": 3 }, { diff --git a/frontend/public/sampledata.json b/frontend/public/sampledata.json index 7140af8b..17d20e24 100644 --- a/frontend/public/sampledata.json +++ b/frontend/public/sampledata.json @@ -65,7 +65,7 @@ "fbLink": "www.facebook.com/events/965341583633198", "driverGender": "Female", "carType": "SUV", - "status": "Closed", + "status": "Ended", "facilitatorId": 1 }, { @@ -111,7 +111,7 @@ "fbLink": "www.facebook.com/events/965341583633198", "driverGender": "All", "carType": "SUV", - "status": "Closed", + "status": "Ended", "facilitatorId": 2 }, { @@ -180,7 +180,7 @@ "fbLink": "www.facebook.com/events/965341583633198", "driverGender": "All", "carType": "Normal", - "status": "Closed", + "status": "Ended", "facilitatorId": 3 }, { diff --git a/frontend/src/facilitator/CreateNewRide.js b/frontend/src/facilitator/CreateNewRide.js index 26132224..76de8a1c 100644 --- a/frontend/src/facilitator/CreateNewRide.js +++ b/frontend/src/facilitator/CreateNewRide.js @@ -37,7 +37,7 @@ class CreateNewRide extends Component { }, }) .then(res => { - const data = res.data[0]; + const data = res.data; this.setState(data); }); @@ -102,8 +102,8 @@ class CreateNewRide extends Component {