From c598a71001727de8bbf8af1d7371df501598f828 Mon Sep 17 00:00:00 2001 From: douglas Date: Sat, 4 Aug 2018 21:35:08 +1000 Subject: [PATCH] #17 - Added support for modifying the database structure from the code #17 - Added driver table #17 - Added driver_car table #17 - Added driver_ride table #17 - Created initial prototype for sending sms #17 - Moved some mysql configurations #30 - Added support for modifying the database structure from the code #30 - Refactored code to create and list rides. #30 - Added missing tests. #30 - Moved backend sources. #30 - Implemented express application for running locally. #30 - Reestructured backend code to use ES6 classes --- README.md | 5 + .../services => backend}/.gitignore | 0 backend/package-lock.json | 1304 +++++++++++++++++ backend/package.json | 27 + .../services => backend}/props.json | 0 .../services => backend}/public_key | 38 +- backend/readme.md | 54 + .../services => backend}/serverless.yml | 8 + .../src/main}/auth/auth.js | 0 .../src/main}/auth/loggedin.js | 0 backend/src/main/controller/awsLambdaApis.js | 16 + backend/src/main/database/DatabaseManager.js | 92 ++ backend/src/main/database/RefreshDatabase.js | 30 + .../changes/2018-08-04-1-create-database.sql | 1 + .../2018-08-04-2-create-location-table.sql | 7 + .../2018-08-04-3-create-rides-table.sql | 21 + .../2018-08-04-4-create-driver-table.sql | 7 + .../2018-08-04-5-create-driver_car-table.sql | 8 + .../2018-08-04-6-create-driver_ride-table.sql | 10 + backend/src/main/database/index.js | 11 + backend/src/main/notification/notify.js | 37 + backend/src/main/rides/Coordinates.js | 8 + backend/src/main/rides/CreateRideService.js | 52 + backend/src/main/rides/FindOneRideService.js | 38 + backend/src/main/rides/ListRidesService.js | 39 + backend/src/main/rides/RideMapper.js | 66 + backend/src/main/rides/RideRepository.js | 137 ++ backend/src/main/rides/RideStatus.js | 8 + backend/src/main/rides/UpdateRideService.js | 60 + .../src/main/rides/aws/AwsLambdaRideApis.js | 47 + .../src/main}/schema/ride.json | 0 backend/src/main/utils/jwt.js | 26 + .../src/main}/utils/ping.js | 0 backend/src/test/RandomUtils.js | 18 + backend/src/test/auth/ExpressAuthApis.js | 122 ++ .../test/config/express/certs/certificate.p12 | Bin 0 -> 2365 bytes .../test/config/express/certs/certificate.pem | 18 + .../src/test/config/express/certs/jwks.json | 17 + backend/src/test/config/express/certs/key.pem | 28 + .../src/test/config/express/certs/private.key | 51 + .../src/test/config/express/certs/public.key | 1 + backend/src/test/expressApis.js | 57 + .../test/rides/CreateRide.integration.test.js | 86 ++ .../rides/FindOneRide.integration.test.js | 112 ++ .../test/rides/ListRides.integration.test.js | 115 ++ backend/src/test/rides/RideEntityBuilder.js | 38 + .../src/test/rides/RideMapper.unit.test.js | 110 ++ backend/src/test/rides/RideTestRepository.js | 15 + .../test/rides/UpdateRide.integration.test.js | 110 ++ .../test/rides/express/ExpressRidesApis.js | 60 + backend/src/test/users/admin.json | 5 + backend/src/test/users/driver.json | 6 + backend/src/test/users/facilitator.json | 5 + doc/carpal-api.yaml | 2 +- doc/sampledata.json | 6 +- frontend/public/sampledata.json | 6 +- frontend/src/facilitator/CreateNewRide.js | 6 +- frontend/src/facilitator/index.js | 4 +- infrastructure/readme.md | 1 - infrastructure/services.md | 23 - infrastructure/services/package-lock.json | 608 -------- infrastructure/services/package.json | 14 - infrastructure/services/rides/create.js | 91 -- infrastructure/services/rides/findone.js | 54 - infrastructure/services/rides/list.js | 67 - infrastructure/services/rides/ride-status.js | 4 - infrastructure/services/rides/rides-mapper.js | 27 - infrastructure/services/rides/update.js | 83 -- infrastructure/services/utils/db.js | 11 - infrastructure/services/utils/jwt.js | 26 - 70 files changed, 3125 insertions(+), 1039 deletions(-) rename {infrastructure/services => backend}/.gitignore (100%) create mode 100644 backend/package-lock.json create mode 100644 backend/package.json rename {infrastructure/services => backend}/props.json (100%) rename {infrastructure/services => backend}/public_key (98%) create mode 100644 backend/readme.md rename {infrastructure/services => backend}/serverless.yml (91%) rename {infrastructure/services => backend/src/main}/auth/auth.js (100%) rename {infrastructure/services => backend/src/main}/auth/loggedin.js (100%) create mode 100644 backend/src/main/controller/awsLambdaApis.js create mode 100644 backend/src/main/database/DatabaseManager.js create mode 100644 backend/src/main/database/RefreshDatabase.js create mode 100644 backend/src/main/database/changes/2018-08-04-1-create-database.sql create mode 100644 backend/src/main/database/changes/2018-08-04-2-create-location-table.sql create mode 100644 backend/src/main/database/changes/2018-08-04-3-create-rides-table.sql create mode 100644 backend/src/main/database/changes/2018-08-04-4-create-driver-table.sql create mode 100644 backend/src/main/database/changes/2018-08-04-5-create-driver_car-table.sql create mode 100644 backend/src/main/database/changes/2018-08-04-6-create-driver_ride-table.sql create mode 100644 backend/src/main/database/index.js create mode 100644 backend/src/main/notification/notify.js create mode 100644 backend/src/main/rides/Coordinates.js create mode 100644 backend/src/main/rides/CreateRideService.js create mode 100644 backend/src/main/rides/FindOneRideService.js create mode 100644 backend/src/main/rides/ListRidesService.js create mode 100644 backend/src/main/rides/RideMapper.js create mode 100644 backend/src/main/rides/RideRepository.js create mode 100644 backend/src/main/rides/RideStatus.js create mode 100644 backend/src/main/rides/UpdateRideService.js create mode 100644 backend/src/main/rides/aws/AwsLambdaRideApis.js rename {infrastructure/services => backend/src/main}/schema/ride.json (100%) create mode 100644 backend/src/main/utils/jwt.js rename {infrastructure/services => backend/src/main}/utils/ping.js (100%) create mode 100644 backend/src/test/RandomUtils.js create mode 100644 backend/src/test/auth/ExpressAuthApis.js create mode 100644 backend/src/test/config/express/certs/certificate.p12 create mode 100644 backend/src/test/config/express/certs/certificate.pem create mode 100644 backend/src/test/config/express/certs/jwks.json create mode 100644 backend/src/test/config/express/certs/key.pem create mode 100644 backend/src/test/config/express/certs/private.key create mode 100644 backend/src/test/config/express/certs/public.key create mode 100644 backend/src/test/expressApis.js create mode 100644 backend/src/test/rides/CreateRide.integration.test.js create mode 100644 backend/src/test/rides/FindOneRide.integration.test.js create mode 100644 backend/src/test/rides/ListRides.integration.test.js create mode 100644 backend/src/test/rides/RideEntityBuilder.js create mode 100644 backend/src/test/rides/RideMapper.unit.test.js create mode 100644 backend/src/test/rides/RideTestRepository.js create mode 100644 backend/src/test/rides/UpdateRide.integration.test.js create mode 100644 backend/src/test/rides/express/ExpressRidesApis.js create mode 100644 backend/src/test/users/admin.json create mode 100644 backend/src/test/users/driver.json create mode 100644 backend/src/test/users/facilitator.json delete mode 100644 infrastructure/services.md delete mode 100644 infrastructure/services/package-lock.json delete mode 100644 infrastructure/services/package.json delete mode 100644 infrastructure/services/rides/create.js delete mode 100644 infrastructure/services/rides/findone.js delete mode 100644 infrastructure/services/rides/list.js delete mode 100644 infrastructure/services/rides/ride-status.js delete mode 100644 infrastructure/services/rides/rides-mapper.js delete mode 100644 infrastructure/services/rides/update.js delete mode 100644 infrastructure/services/utils/db.js delete mode 100644 infrastructure/services/utils/jwt.js 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/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 00000000..8d2653df --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,1304 @@ +{ + "name": "carpal", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array-uniq": { + "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", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "1.0.0", + "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 + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sdk": { + "version": "2.250.1", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.250.1.tgz", + "integrity": "sha1-DMBO38QfoKal7MvFaIiDL1knAUE=", + "dev": true, + "requires": { + "buffer": "4.9.1", + "events": "1.1.1", + "ieee754": "1.1.8", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.1.0", + "xml2js": "0.4.17" + }, + "dependencies": { + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "dev": true + } + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bignumber.js": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.0.4.tgz", + "integrity": "sha512-LDXpJKVzEx2/OqNbG9mXBNvHuiRL4PzHCGfnANHMJ+fv68Ads3exDVJeGDJws+AoNEuca93bU3q+S0woeUaCdg==" + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "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=" + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "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", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "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 + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "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==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "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=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "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" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "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" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "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 + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", + "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "express": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", + "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.3", + "qs": "6.5.1", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.1", + "http-errors": "~1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "~2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "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==" + }, + "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==" + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "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=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": ">= 1.3.1 < 2" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "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==" + } + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.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=" + } + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "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 + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "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", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "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=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonschema": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.4.tgz", + "integrity": "sha512-lz1nOH69GbsVHeVgEdvyavc/33oymY1AZwtePMiMj4HZPMbP5OIKK3zT9INMWjwua/V4Z4yq7wSlBbSG+g4AEw==" + }, + "jsonwebtoken": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.2.2.tgz", + "integrity": "sha512-rFFq7ow/JpPzwgaz4IyRL9cp7f4ptjW92eZgsQyqkysLBmDjSSBhnKfQESoq0GU+qJXK/CQ0o4shgwbUPiFCdw==", + "requires": { + "jws": "^3.1.5", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "xtend": "^4.0.1" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jwa": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz", + "integrity": "sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.10", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", + "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", + "requires": { + "jwa": "^1.1.5", + "safe-buffer": "^5.0.1" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "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=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "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==" + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "requires": { + "mime-db": "~1.33.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "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=" + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "mysql": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.15.0.tgz", + "integrity": "sha512-C7tjzWtbN5nzkLIV+E8Crnl9bFyc7d3XJcBAvHKEVkjrYjogz3llo22q6s/hw+UcsE4/844pDob9ac+3dVjQSA==", + "requires": { + "bignumber.js": "4.0.4", + "readable-stream": "2.3.3", + "safe-buffer": "5.1.1", + "sqlstring": "2.3.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + } + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "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=" + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "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=" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" + } + }, + "request": { + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", + "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=", + "dev": true + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "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=" + } + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "sqlstring": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.0.tgz", + "integrity": "sha1-UluKT9Jtb3GqYegipsr5dtMa0qg=" + }, + "sshpk": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", + "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "tweetnacl": "~0.14.0" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "requires": { + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "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==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xml2js": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", + "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "^4.1.0" + } + }, + "xmlbuilder": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", + "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", + "dev": true, + "requires": { + "lodash": "^4.0.0" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 00000000..53df909c --- /dev/null +++ b/backend/package.json @@ -0,0 +1,27 @@ +{ + "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", + "refresh-test-db": "node ./src/test/database/refreshDatabase.js" + }, + "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" + } +} 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 91% rename from infrastructure/services/serverless.yml rename to backend/serverless.yml index 3f6665c0..c164d29e 100644 --- a/infrastructure/services/serverless.yml +++ b/backend/serverless.yml @@ -9,8 +9,11 @@ provider: 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: @@ -86,6 +89,11 @@ functions: 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/controller/awsLambdaApis.js b/backend/src/main/controller/awsLambdaApis.js new file mode 100644 index 00000000..e05c7be2 --- /dev/null +++ b/backend/src/main/controller/awsLambdaApis.js @@ -0,0 +1,16 @@ +const CreateRideService = require('../rides/CreateRideService'); +const ListRidesService = require('../rides/ListRidesService'); +const FindOneRideService = require('../rides/FindOneRideService'); +const DatabaseManager = require('../database/DatabaseManager'); +const AwsLambdaRideApis = require('../rides/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: rides +}; \ No newline at end of file diff --git a/backend/src/main/database/DatabaseManager.js b/backend/src/main/database/DatabaseManager.js new file mode 100644 index 00000000..fa3b4eb0 --- /dev/null +++ b/backend/src/main/database/DatabaseManager.js @@ -0,0 +1,92 @@ +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.finally(() => { + if (error) { + return reject(error); + } + resolve(results); + }); + } + 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..46f731e1 --- /dev/null +++ b/backend/src/main/database/RefreshDatabase.js @@ -0,0 +1,30 @@ +const fs = require('fs'); +const path = require('path'); + +const changeSet = [ + '2018-08-04-1-create-database.sql', + '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) { + this.databaseManager = databaseManager; + } + + async executeAll() { + for (let fileName of changeSet) { + await this.execute(fileName); + } + } + + async execute(fileName) { + let sql = fs.readFileSync(path.resolve(__dirname, './changes/' + fileName)).toString().trim(); + return this.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..95e1904f --- /dev/null +++ b/backend/src/main/database/changes/2018-08-04-1-create-database.sql @@ -0,0 +1 @@ +CREATE DATABASE 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..db4ae7a1 --- /dev/null +++ b/backend/src/main/rides/CreateRideService.js @@ -0,0 +1,52 @@ +'use strict'; + +const jsonValidator = require('jsonschema'); +const rideSchema = require('../schema/ride.json'); +const RideStatus = require('./RideStatus'); +const RideRepository = require('./RideRepository'); +const RideMapper = require('./RideMapper'); + +class CreateRideService { + + constructor(databaseManager) { + this._databaseManager = databaseManager; + this._rideRepository = new RideRepository(databaseManager); + } + + createRide(body, loginData) { + const connection = this._databaseManager.createConnection(); + + return this._createRide(body, loginData, connection) + .finally(() => this._databaseManager.closeConnection(connection)); + } + + _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..f53981f4 --- /dev/null +++ b/backend/src/main/rides/FindOneRideService.js @@ -0,0 +1,38 @@ +'use strict'; + +const RideRepository = require('./RideRepository'); +const RidesMapper = require('./RideMapper'); + +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(); + return this._findOne(id, loginData, connection) + .finally(() => this._databaseManager.closeConnection(connection)); + } + + _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..48b20115 --- /dev/null +++ b/backend/src/main/rides/ListRidesService.js @@ -0,0 +1,39 @@ +'use strict'; + +const RideRepository = require('./RideRepository'); +const RidesMapper = require('./RideMapper'); + +class ListRidesService { + + constructor(databaseManager) { + this._databaseManager = databaseManager; + this._rideRepository = new RideRepository(databaseManager); + } + + listRides(queryParams, loginData) { + const connection = this._databaseManager.createConnection(); + + return this._listRides(queryParams, loginData, connection) + .then(rides => rides.map(RidesMapper.entityToDto)) + .finally(() => this._databaseManager.closeConnection(connection)); + } + + _listRides(queryParams, loginData, connection) { + let jsonQuery = this._parseParams(queryParams, loginData); + return this._rideRepository.list(jsonQuery, connection); + } + + _parseParams(queryParams, loginData) { + return { + toLongitude: queryParams.toLongitude, + toLatitude: queryParams.toLatitude, + fromLongitude: queryParams.fromLongitude, + fromLatitude: queryParams.fromLatitude, + driverGenders: loginData.role === 'driver' ? ['any', loginData.driverGender] : undefined, + includePickupTimeInPast: loginData.role !== 'driver', + facilitatorId: loginData.role === 'facilitator' ? loginData.email : undefined, + }; + } +} + +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..520edab7 --- /dev/null +++ b/backend/src/main/rides/RideRepository.js @@ -0,0 +1,137 @@ +'use strict'; + +const moment = require('moment'); + +class RideRepository { + constructor(databaseManager) { + this._databaseManager = databaseManager; + } + + 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 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 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 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..08f331f5 --- /dev/null +++ b/backend/src/main/rides/UpdateRideService.js @@ -0,0 +1,60 @@ +'use strict'; + +const jsonValidator = require('jsonschema'); +const rideSchema = require('../schema/ride.json'); +const RideRepository = require('./RideRepository'); +const RideMapper = require('./RideMapper'); + +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); + return updatePromise.finally(() => this._databaseManager.closeConnection(connection)); + } + + _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/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/jwt.js b/backend/src/main/utils/jwt.js new file mode 100644 index 00000000..27586656 --- /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.role = decodedToken[`https://${domain}/role`]; + if (claims.role === 'driver') { + 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..bd64c332 --- /dev/null +++ b/backend/src/test/auth/ExpressAuthApis.js @@ -0,0 +1,122 @@ +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) { + let expiry = moment().add(100, 'd'); + 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.role; + 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 0000000000000000000000000000000000000000..59e3a11f8b96841e738542bdb42ea2127b2a732e GIT binary patch literal 2365 zcmV-D3BvX;f(bbS0Ru3C2>%8NDuzgg_YDCD0ic2i@C1Sg>@b1|=rDo99ozaTW57?$!^lQAJMAC4(o-#HH&Bt-E#GTT3H%X~l-$*}ssnDz z3IvSDKpGHH@R<6`WJ#Z>b_eXaxY~LR)aK{&Uc*?Nuri`BM`a>snovM$BZB9XSv`T* zgI0a3;2@k2MS%`L<|Fhxe1>0woBpi!Saw(HZ2n*p1g}F!i3ehn-h_Y3buk&HiJtgt zLd%1-OH*Ugb?h?oHy5>t9|P_K1@M&Oscwm8jHCU3sObtK(Lgn&SHGA|7K$h>8t$0I z9W3+ocZ3Ak*{uS(lvyqa9L$L4736}oUdg!&xgC;eN}#cgkh<+JOjRtib<1~o-v3+W zDTW2bsz+&*)3xmcid6T&V14*(5AmnlV0A+h{y!X()90tM)e%eT7#026`$9*7@u^Y%c-;60!cY3 zy0BRMBChm-%F?N&Ik2T=;n?i7e`U6c5pEj+uO6XJEv9FPW^#HB$zLKZ_NYpFWLn7R zeV`WslD$PS#b=3ytYJEJNJu%jvhoW&30)L`=KQngG3dfTbNf%b{H8n%iyWZJe(tab z8mVZUaf7cW`H)z-)O(QE7?wbSFx#@)8^>B-@s?b5J2Dif#u$#38v{!hAXE8vA_cOb zXto=lnN&;D8;nN3bb}^-->Z4_)h+gdn`Dq0tX?wD@cG+qD%gIFcKzOi9`7 zqO{zngFceUbEfTPJ%+dcANwPKp<)!szS4qW-4{S@wrhs7)a(7O-k*F45 z1opH-PY^dd5uu@l8tCYLgzQXC4oWmDoSAE`s^UOgS%7t(0+M&&LNQU>7r4-_-e#%2&vYYRBj6*9@Y4o6wf0)V@)T&I3w8o zdrmvMQEWY}xpR8zfB9+3kTS5lB`AieMww;iFhS#mdjvEs^e40FA z!E%K27-_|BI4Lws^f(i6#5@88+YhLhaMWd)L!&W+Ldq*5f{|G7_KpJh4InzIc*&=n zBbPJo=e*EPXGnk8Xsvx_WXSz42|dF0=DCw~M^S*WbR- zi8F#`krd~DmvV@zpG%Bdy?foYwzYU{Z1Wk#=xwNUFF0P*R7YQXI-MF=ET~Lzp@ulg z7&cTNsp7%B`+(2T0yN+yAoUuoR=_ccU%2i#d1PkMA`Pj)LS+j$;Y#drNmsk5<+4%x zL!-axMUcqK3aaLg4}l~In4kHXC%vJ1fTdRXE56A`_g0IK!~r#iAJ?s?HC12j8a&1N zS;~z=P{`#MrO?HZkd%Ebr+Ca{H+#H^NEX%~uVDL4XU+_UH7&3Ip>5%+(<6hOzkld> zQ&0DDGFSc{pT-aOj)#@bgH^fCh6L`q!jO^< z$NduUQ5A?cw!14wdw|kJgZx{bSzfs-FPjdJk;O@Z^s3QVmZI38S@EGTwyJQMc>SyK*bo8}#iDQ6(k6&yzX75+F|1pRe-2bY zKx;mOqVnW}1XI@atNQl=oieLB&myh|BQlV-=UfzsnLhMO*zg!<$><$h%7qvVC?jUJNZHgjmn-&|NEo$GB)RTP8T^5)q?3=8;8*%vqq2Uohv$?$EHGM9)Y2E?h{W zFJ6#`dHku~dB>4thU9}W1s&Okto*~7dz0<}OKd@o)1rP>D>9uGmD`Oz#KNHeUaI7~ zxaE07qk;?+g)V)8F98|FqDvHV|WMf@hAKg*vZ;shamskBsYh~(&E4g z$!ZlrIjV+8u<>e*%BZg;9L1RVE_hUhXFT_7bu9~3Eh-5^mBAZt#XJ { + 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..3459c250 --- /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(); + mockDatabaseManager = new DatabaseManager(); + connection = databaseManager.createConnection() ; + mockDatabaseManager.createConnection = () => connection; + mockDatabaseManager.closeConnection = () => null; +}); + +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..42d96784 --- /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(), role: 'facilitator'}; + const email = loginData.email; + const ride1 = randomRideWithFacilitator(email); + const ride2 = randomRideWithFacilitator(email); + await databaseContainsRides(ride1, ride2); + + // when + const rides = await listRideController.listRides({}, loginData); + + assert.deepEqualExcluding(rides, [ride1, ride2], 'id'); + }); + + it('should show all rides for admin', async function () { + // given + const loginData = {email: RandomUtils.randomEmail(), role: '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(), role: '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..22fa25bc --- /dev/null +++ b/backend/src/test/users/admin.json @@ -0,0 +1,5 @@ +{ + "email": "test-admin@carpal.com", + "gender": "male", + "role": "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..4b1948f1 --- /dev/null +++ b/backend/src/test/users/driver.json @@ -0,0 +1,6 @@ +{ + "email": "test-driver@carpal.com", + "gender": "male", + "car": "suv", + "role": "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..f74e3445 --- /dev/null +++ b/backend/src/test/users/facilitator.json @@ -0,0 +1,5 @@ +{ + "email": "test-facilitator@carpal.com", + "gender": "female", + "role": "facilitator" +} \ No newline at end of file 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 { this.setState({ pickupTimeAndDateInUTC: date })} showTimeSelect timeFormat="HH:mm" diff --git a/frontend/src/facilitator/index.js b/frontend/src/facilitator/index.js index e8e247e6..47f54324 100644 --- a/frontend/src/facilitator/index.js +++ b/frontend/src/facilitator/index.js @@ -93,7 +93,9 @@ const columns = [ className="custom-select" > - + + + ), diff --git a/infrastructure/readme.md b/infrastructure/readme.md index b863ffb3..c485da37 100644 --- a/infrastructure/readme.md +++ b/infrastructure/readme.md @@ -6,4 +6,3 @@ The CarPal infrastructure is based on the following Amazon Web Services (AWS): - MySQL relational database service (RDS) In addition authentication is based on Auth0 and JSON Web Tokens (JWTs). - diff --git a/infrastructure/services.md b/infrastructure/services.md deleted file mode 100644 index 0823afc8..00000000 --- a/infrastructure/services.md +++ /dev/null @@ -1,23 +0,0 @@ -# Services -The services are created with Serverless and a few manual steps. - -## Initial Setup - -### 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/package-lock.json b/infrastructure/services/package-lock.json deleted file mode 100644 index 57e9bd07..00000000 --- a/infrastructure/services/package-lock.json +++ /dev/null @@ -1,608 +0,0 @@ -{ - "name": "carpal", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" - } - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sdk": { - "version": "2.250.1", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.250.1.tgz", - "integrity": "sha1-DMBO38QfoKal7MvFaIiDL1knAUE=", - "dev": true, - "requires": { - "buffer": "4.9.1", - "events": "1.1.1", - "ieee754": "1.1.8", - "jmespath": "0.15.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "uuid": "3.1.0", - "xml2js": "0.4.17" - }, - "dependencies": { - "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", - "dev": true - } - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" - }, - "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "bignumber.js": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.0.4.tgz", - "integrity": "sha512-LDXpJKVzEx2/OqNbG9mXBNvHuiRL4PzHCGfnANHMJ+fv68Ads3exDVJeGDJws+AoNEuca93bU3q+S0woeUaCdg==" - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "1.3.0", - "ieee754": "1.1.8", - "isarray": "1.0.0" - } - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "requires": { - "delayed-stream": "1.0.0" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "1.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "ecdsa-sig-formatter": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", - "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.6", - "mime-types": "2.1.18" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "1.0.0" - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.14.1" - } - }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", - "dev": true - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jmespath": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=", - "dev": true - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsonschema": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.4.tgz", - "integrity": "sha512-lz1nOH69GbsVHeVgEdvyavc/33oymY1AZwtePMiMj4HZPMbP5OIKK3zT9INMWjwua/V4Z4yq7wSlBbSG+g4AEw==" - }, - "jsonwebtoken": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.2.2.tgz", - "integrity": "sha512-rFFq7ow/JpPzwgaz4IyRL9cp7f4ptjW92eZgsQyqkysLBmDjSSBhnKfQESoq0GU+qJXK/CQ0o4shgwbUPiFCdw==", - "requires": { - "jws": "3.1.5", - "lodash.includes": "4.3.0", - "lodash.isboolean": "3.0.3", - "lodash.isinteger": "4.0.4", - "lodash.isnumber": "3.0.3", - "lodash.isplainobject": "4.0.6", - "lodash.isstring": "4.0.1", - "lodash.once": "4.1.1", - "ms": "2.1.1", - "xtend": "4.0.1" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jwa": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz", - "integrity": "sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.10", - "safe-buffer": "5.1.2" - } - }, - "jws": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", - "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", - "requires": { - "jwa": "1.1.6", - "safe-buffer": "5.1.2" - } - }, - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", - "dev": true - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" - }, - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" - }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "requires": { - "mime-db": "1.33.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "mysql": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.15.0.tgz", - "integrity": "sha512-C7tjzWtbN5nzkLIV+E8Crnl9bFyc7d3XJcBAvHKEVkjrYjogz3llo22q6s/hw+UcsE4/844pDob9ac+3dVjQSA==", - "requires": { - "bignumber.js": "4.0.4", - "readable-stream": "2.3.3", - "safe-buffer": "5.1.1", - "sqlstring": "2.3.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" - } - } - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - }, - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.2", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.87.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", - "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", - "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.7.0", - "caseless": "0.12.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.2", - "har-validator": "5.0.3", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.18", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.2", - "safe-buffer": "5.1.2", - "tough-cookie": "2.3.4", - "tunnel-agent": "0.6.0", - "uuid": "3.2.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=", - "dev": true - }, - "sqlstring": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.0.tgz", - "integrity": "sha1-UluKT9Jtb3GqYegipsr5dtMa0qg=" - }, - "sshpk": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", - "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", - "requires": { - "punycode": "1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true - }, - "url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - } - }, - "xml2js": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", - "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", - "dev": true, - "requires": { - "sax": "1.2.1", - "xmlbuilder": "4.2.1" - } - }, - "xmlbuilder": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", - "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", - "dev": true, - "requires": { - "lodash": "4.17.10" - } - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" - } - } -} diff --git a/infrastructure/services/package.json b/infrastructure/services/package.json deleted file mode 100644 index f92cd03f..00000000 --- a/infrastructure/services/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "carpal", - "version": "1.0.0", - "dependencies": { - "jsonschema": "^1.2.4", - "jsonwebtoken": "^8.1.0", - "request": "^2.87.0", - "uuid": "^3.2.1", - "mysql": "^2.15.0" - }, - "devDependencies": { - "aws-sdk": "^2.250.1" - } -} diff --git a/infrastructure/services/rides/create.js b/infrastructure/services/rides/create.js deleted file mode 100644 index 7e201824..00000000 --- a/infrastructure/services/rides/create.js +++ /dev/null @@ -1,91 +0,0 @@ -'use strict'; - -const validate = require('jsonschema').validate; // eslint-disable-line import/no-extraneous-dependencies -const rideSchema = require('../schema/ride.json'); // eslint-disable-line import/no-extraneous-dependencies -const db = require('../utils/db').connection; -const rideStatus = require('./ride-status'); -const decodeJwt = require('../utils/jwt').decodeJwt; - -module.exports.create = (event, context, callback) => { - const timestamp = new Date().getTime(); - const connection = db(); - - const data = JSON.parse(event.body); - data.datetime = timestamp; - - let loginData = decodeJwt(event); - let facilitatorEmail = loginData && loginData.email; - const validationResult = validate(data, rideSchema); - - if (validationResult.errors && validationResult.errors.length) { - callback(null, { - statusCode: 400, - headers: { - 'Content-Type': 'text/plain' - }, - body: JSON.stringify({"error": validationResult.errors}) - }); - return; - } - - let escape = function (data) { - return connection.escape(data) - }; - let payload = { - client: `${escape(data.client)}`, - facilitatorEmail: `${escape(facilitatorEmail)}`, - pickupTimeAndDateInUTC: `"${new Date(data.pickupTimeAndDateInUTC).toISOString()}"`, - locationFrom: `POINT(${data.locationFrom.latitude}, ${data.locationFrom.longitude})`, - locationTo: `POINT(${data.locationTo.latitude}, ${data.locationTo.longitude})`, - fbLink: `${escape(data.fbLink)}`, - driverGender: `${escape(data.driverGender)}`, - carType: `${escape(data.carType)}`, - status: `"${rideStatus.open}"`, - deleted: `0`, - suburbFrom: `${escape(data.locationFrom.suburb)}`, - placeNameFrom: `${escape(data.locationFrom.placeName)}`, - postCodeFrom: `${escape(data.locationFrom.postcode)}`, - suburbTo: `${escape(data.locationTo.suburb)}`, - placeNameTo: `${escape(data.locationTo.placeName)}`, - postCodeTo: `${escape(data.locationTo.postcode)}`, - description: `${escape(data.description)}` - }; - - let values = Reflect - .ownKeys(payload) - .map(key => payload[key]) - .join(','); - let query = `INSERT INTO rides(${Reflect.ownKeys(payload)}) VALUES (${values})`; - console.log(query); - - queryDatabase(connection, query, (err, result) => { - if (err) { - console.log('ERROR', err); - return callback(null, { - statusCode: err.statusCode || 501, - headers: { - 'Content-Type': 'text/plain' - }, - body: JSON.stringify(err) - }); - } - callback(null, { - statusCode: 200, - headers: { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Credentials': true, - 'Content-Type': 'text/plain' - }, - body: JSON.stringify(result) - }) - }); -}; - -function queryDatabase(connection, query, callback) { - connection.query(query, (error, results, fields) => { - connection - .end(function (err) { - callback(error, results); - }); - }); -} diff --git a/infrastructure/services/rides/findone.js b/infrastructure/services/rides/findone.js deleted file mode 100644 index 4ddfa330..00000000 --- a/infrastructure/services/rides/findone.js +++ /dev/null @@ -1,54 +0,0 @@ -'use strict'; -const db = require('../utils/db').connection; -const mapToDto = require('./rides-mapper').mapToDto; - -module.exports.findone = (event, context, callback) => { - console.log('Querying mysql'); - const connection = db(); - let id = event.pathParameters.id; - if (!id) { - return callback(null, { - statusCode: 400, - headers: { - 'Content-Type': 'text/plain' - }, - body: 'Invalid id' - }); - } - - queryDatabase(connection, `SELECT * FROM rides where id = ${id}`, (error, result) => { - if (error) { - console.error(error); - callback(null, { - statusCode: error.statusCode || 501, - headers: { - 'Content-Type': 'text/plain' - }, - body: JSON.stringify(error) - }); - return; - } - console.log(mapToDto(result)); - - // create a response - const response = { - statusCode: 200, - headers: { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Credentials': true - }, - body: JSON.stringify(mapToDto(result)) - }; - callback(null, response); - }); - -}; - - -function queryDatabase(connection, query, callback) { - connection.query(query, (error, results, fields) => { - connection.end(function (err) { - callback(error, results); - }); - }); -} \ No newline at end of file diff --git a/infrastructure/services/rides/list.js b/infrastructure/services/rides/list.js deleted file mode 100644 index 9d77ff7f..00000000 --- a/infrastructure/services/rides/list.js +++ /dev/null @@ -1,67 +0,0 @@ -'use strict'; -const decodeJwt = require('../utils/jwt').decodeJwt; -const db = require('../utils/db').connection; -const mapToDto = require('./rides-mapper').mapToDto; - -module.exports.list = (event, context, callback) => { - let queryParams = event.queryStringParameters || {}; - console.log(JSON.stringify(queryParams)); - console.log('Querying mysql'); - const connection = db(); - const claims = decodeJwt(event); - console.log("Claims: " + JSON.stringify(claims)); - - let query; - - if (claims.role === 'driver') { - let locationQuery = ''; - if (queryParams.toLongitude && queryParams.toLatitude && queryParams.fromLongitude && queryParams.fromLatitude) { - locationQuery = `and ST_Contains(ST_Envelope(ST_GeomFromText('LINESTRING(${queryParams.toLongitude} ${queryParams.toLatitude}, ${queryParams.fromLongitude} ${queryParams.fromLatitude})')), locationFrom)`; - } - query = ` -SELECT * FROM rides where pickupTimeAndDateInUTC > NOW() -and(driverGender = 'any' or driverGender = '${claims.driverGender}') -${locationQuery} -ORDER BY pickupTimeAndDateInUTC ASC - ` - } else if (claims.role === 'facilitator') { - query = ` -SELECT * FROM carpal.rides WHERE facilitatorEmail = '${claims.email}' -ORDER BY pickupTimeAndDateInUTC ASC; - ` - } else if (claims.role === 'admin') { - query = ` -SELECT * FROM carpal.rides -ORDER BY pickupTimeAndDateInUTC ASC; - ` - } - - console.log('Query: ' + query); - - connection.query(query, function (error, results, fields) { - if (error) { - console.error(error); - callback(null, { - statusCode: error.statusCode || 501, - headers: { - 'Content-Type': 'text/plain' - }, - body: 'Couldn\'t fetch rides.' - }); - return; - } - console.log(results); - const response = { - statusCode: 200, - headers: { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Credentials': true - }, - body: JSON.stringify(mapToDto(results)) - }; - connection.end(function (err) { - callback(null, response); - }); - }); - -}; \ No newline at end of file diff --git a/infrastructure/services/rides/ride-status.js b/infrastructure/services/rides/ride-status.js deleted file mode 100644 index 7b806d27..00000000 --- a/infrastructure/services/rides/ride-status.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - open: 'open', - closed: 'closed' -}; \ No newline at end of file diff --git a/infrastructure/services/rides/rides-mapper.js b/infrastructure/services/rides/rides-mapper.js deleted file mode 100644 index 1fae2052..00000000 --- a/infrastructure/services/rides/rides-mapper.js +++ /dev/null @@ -1,27 +0,0 @@ -module.exports.mapToDto = (rides) => { - return (rides || []).map(ride => ({ - "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, - "facilitatorId": ride.facilitatorEmail, - "id": ride.id, - "description": ride.description - })) -}; diff --git a/infrastructure/services/rides/update.js b/infrastructure/services/rides/update.js deleted file mode 100644 index 1248f338..00000000 --- a/infrastructure/services/rides/update.js +++ /dev/null @@ -1,83 +0,0 @@ -'use strict'; - -const db = require('../utils/db').connection; -const rideStatus = require('./ride-status'); -const decodeJwt = require('../utils/jwt').decodeJwt; - -module.exports.update = (event, context, callback) => { - const timestamp = new Date().getTime(); - const connection = db(); - - const data = JSON.parse(event.body); - data.datetime = timestamp; - let loginData = decodeJwt(event); - let facilitatorEmail = loginData && loginData.email; - - let id = event.pathParameters.id; - if(!id){ - return callback(null, { - statusCode: 400, - headers: { - 'Content-Type': 'text/plain' - }, - body: 'Invalid id' - }); - } - - let escape = function (data) { - return connection.escape(data) - }; - let payload = { - client: `${escape(data.client)}`, - facilitatorEmail: `${escape(facilitatorEmail)}`, - pickupTimeAndDateInUTC: `"${new Date(data.pickupTimeAndDateInUTC).toISOString()}"`, - locationFrom: `POINT(${data.locationFrom.latitude}, ${data.locationFrom.longitude})`, - locationTo: `POINT(${data.locationTo.latitude}, ${data.locationTo.longitude})`, - fbLink: `${escape(data.fbLink)}`, - driverGender: `${escape(data.driverGender)}`, - carType: `${escape(data.carType)}`, - status: `${escape(data.status)}`, - deleted: `0`, - suburbFrom: `${escape(data.locationFrom.suburb)}`, - placeNameFrom: `${escape(data.locationFrom.placeName)}`, - postCodeFrom: `${escape(data.locationFrom.postcode)}`, - suburbTo: `${escape(data.locationTo.suburb)}`, - placeNameTo: `${escape(data.locationTo.placeName)}`, - postCodeTo: `${escape(data.locationTo.postcode)}`, - description: `${escape(data.description)}` - }; - - let values = Reflect.ownKeys(payload).map(key => `${key} = ${payload[key]}`).join(','); - let query = `UPDATE rides SET ${values} WHERE id = ${id}`; - console.log(query); - - queryDatabase(connection, query, (err, result) => { - if (err) { - console.log('ERROR', err); - return callback(null, { - statusCode: err.statusCode || 501, - headers: { - 'Content-Type': 'text/plain' - }, - body: JSON.stringify(err) - }); - } - callback(null, { - statusCode: 200, - headers: { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Credentials': true, - 'Content-Type': 'text/plain' - }, - body: JSON.stringify(result) - }) - }); -}; - -function queryDatabase(connection, query, callback) { - connection.query(query, (error, results, fields) => { - connection.end(function (err) { - callback(error, results); - }); - }); -} diff --git a/infrastructure/services/utils/db.js b/infrastructure/services/utils/db.js deleted file mode 100644 index b194ea77..00000000 --- a/infrastructure/services/utils/db.js +++ /dev/null @@ -1,11 +0,0 @@ -var mysql = require('mysql'); -const MYSQL_PW = process.env.MYSQL_PW; -const MYSQL_PORT = process.env.MYSQL_PORT; - -module.exports.connection = () => mysql.createConnection({ - host: 'carpal.cttgjqpjknhf.ap-southeast-2.rds.amazonaws.com', - port: MYSQL_PORT, - user: 'carpaladmin', - password: MYSQL_PW, - database: 'carpal' -}); \ No newline at end of file diff --git a/infrastructure/services/utils/jwt.js b/infrastructure/services/utils/jwt.js deleted file mode 100644 index b5c7fbdc..00000000 --- a/infrastructure/services/utils/jwt.js +++ /dev/null @@ -1,26 +0,0 @@ -const jsonwebtoken = require('jsonwebtoken'); - -module.exports.decodeJwt = (event) => { - if (!event.headers.Authorization) { - return; - } - const tokenValue = event - .headers - .Authorization - .split(' ')[1]; - - try { - const decodedToken = jsonwebtoken.decode(tokenValue); - var claims = {}; - claims.email = decodedToken['https://carpal.org.au/email']; - claims.role = decodedToken['https://carpal.org.au/role']; - if (claims.role === 'driver') { - claims.driverGender = decodedToken['https://carpal.org.au/gender']; - claims.car = decodedToken['https://carpal.org.au/car']; - } - return claims; - } catch (err) { - console.log('catch error. Invalid token', err); - return; - } -} \ No newline at end of file