diff --git a/.env.example b/.env.example index ef6afc6..7081eb1 100644 --- a/.env.example +++ b/.env.example @@ -86,16 +86,6 @@ FITBIT_CLIENT_ID=CIENT_ID_HERE # default value: CIENT_SECRET_HERE FITBIT_CLIENT_SECRET=CIENT_SECRET_HERE -# FITBIT_CLIENT_SUBSCRIBER: Client Subscriber code for automatically get notification from new -# sync data. -# default value: CLIENT_SUBSCRIBER_HERE -FITBIT_CLIENT_SUBSCRIBER=CLIENT_SUBSCRIBER_HERE - -# FITBIT_SUBSCRIBER_ID: Customer Subscriber ID, used to manage the subscriber who will -# receive notification of a user resource. -# default value: SUBSCRIBER_ID_HERE -FITBIT_SUBSCRIBER_ID=SUBSCRIBER_ID_HERE - ################################################################################################# ################################# DATA SYNC ENVIRONMENT SETUP ################################### ################################################################################################# diff --git a/README.md b/README.md index 21f73ca..16babd6 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,7 @@ Microservice responsible for data synchronization of FitBit and CVE platform wit - Fitbit access token management; - Automatically sync Fitbit data; - Publish sync data to a message channel; -- Fitbit access token revocation; -- Subscriber to resources provided by Fitbit. +- Fitbit access token revocation. ## Prerequisites - [Node 8.0.0+](https://nodejs.org/en/download/) @@ -27,7 +26,6 @@ Application settings are defined by environment variables. To define the setting | `NODE_ENV` | Defines the environment in which the application runs. You can set: `test` _(in this environment, the database defined in `MONGODB_URI_TEST` is used and the logs are disabled for better visualization of the test output)_, `development` _(in this environment, all log levels are enabled)_ and `production` _(in this environment, only the warning and error logs are enabled)_. | `development` | | `PORT_HTTP` | Port used to listen for HTTP requests. Any request received on this port is redirected to the HTTPS port. | `5000` | | `PORT_HTTPS` | Port used to listen for HTTPS requests. Do not forget to provide the private key and the SSL/TLS certificate. See the topic [generate certificates](#generate-certificates). | `5001` | -| `HOST_WHITELIST` | Access control based on IP addresses. Only allow IP requests in the unlock list. You can define IP or host, for example: `[127.0.0.1, api.ocariot.com]`. To accept requests from any customer, use the character `*`. | `[*]` | | `SSL_KEY_PATH` | SSL/TLS certificate private key. | `.certs/server.key` | | `SSL_CERT_PATH` | SSL/TLS certificate. | `.certs/server.crt` | | `MONGODB_URI` | Database connection URI used if the application is running in development or production environment. The [URI specifications ](https://docs.mongodb.com/manual/reference/connection-string) defined by MongoDB are accepted. For example: `mongodb://user:pass@host:port/database?options` | `mongodb://127.0.0.1:27017`
`/ocariot-ds-agent` | @@ -37,8 +35,6 @@ Application settings are defined by environment variables. To define the setting | `RABBITMQ_CA_PATH` | RabbitMQ CA file location. Must always be provided when using `amqps` protocol. | `.certs/rabbitmqca.crt` | | `FITBIT_CLIENT_ID` | Client Id for Fitbit Application resposible to manage user data. | `CIENT_ID_HERE` | | `FITBIT_CLIENT_SECRET` | Client Secret for Fitbit Application resposible to manage user data. | `CIENT_SECRET_HERE` | -| `FITBIT_CLIENT_SUBSCRIBER` | Code used by Fitbit to verify the subscriber. | `CLIENT_SUBSCRIBER_HERE` | -| `FITBIT_SUBSCRIBER_ID` | Customer Subscriber ID, used to manage the subscriber who will receive notification of a user resource. | `FITBIT_SUBSCRIBER_ID` | | `EXPRESSION_AUTO_SYNC` | Defines how often the application will automatically sync user data in the background according to the crontab expression. | `0 0 * * 0` | ## Generate Certificates @@ -114,7 +110,6 @@ You can also create the container by passing the settings that are desired by th docker run --rm \ -e PORT_HTTP=8080 \ -e PORT_HTTPS=8081 \ - -e HOST_WHITELIST="[localhost]" \ -e SSL_KEY_PATH=.certs/server.key \ -e SSL_CERT_PATH=.certs/server.crt \ -e RABBITMQ_URI="amqp://guest:guest@192.168.0.1:5672" \ @@ -122,8 +117,6 @@ docker run --rm \ -e REDIS_URI="redis://127.0.0.1:6379" \ -e FITBIT_CLIENT_ID="YOUR_FITBIT_CLIENT_ID" \ -e FITBIT_CLIENT_SECRET="YOUR_FITBIT_CLIENT_SECRET" \ - -e FITBIT_CLIENT_SUBSCRIBER="YOUR_FITBIT_CLIENT_SUBSCRIBER" \ - -e FITBIT_SUBSCRIBER_ID="SUBSCRIBER_ID_HERE" \ -e EXPRESSION_AUTO_SYNC="0 0 * * 0" \ ocariot/ds-agent ``` @@ -136,8 +129,6 @@ docker run --rm \ -e REDIS_URI="redis://127.0.0.1:6379" \ -e FITBIT_CLIENT_ID="YOUR_FITBIT_CLIENT_ID" \ -e FITBIT_CLIENT_SECRET="YOUR_FITBIT_CLIENT_SECRET" \ - -e FITBIT_CLIENT_SUBSCRIBER="YOUR_FITBIT_CLIENT_SUBSCRIBER" \ - -e FITBIT_SUBSCRIBER_ID="SUBSCRIBER_ID_HERE" \ -e EXPRESSION_AUTO_SYNC="0 0 * * 0" \ ocariot/ds-agent ``` @@ -161,14 +152,14 @@ This microservice has a particular way of managing errors from the Fitbit Client ``` The `code` parameter is an internal implementation of the API, which serves to map the generated errors. The following table illustrates the mapping of these errors as implemented in the API: -| code | type | reference | message | description | -|-----|-----|-----|-----|-----| -|1011| expired_token | Expired Access Token | Access token expired. | The access token has been expired and needs to be refreshed. | -|1012| invalid_token | Invalid Access Token | Access token invalid. | The access token is invalid. Please make a new Fitbit Auth Data request and try again. | -|1021| invalid_grant | Invalid Refresh Token | Refresh token invalid. | The refresh token is invalid. Please make a new Fitbit Auth Data request and try again. | -|1401| invalid_client | Invalid Client Credentials | Invalid Fitbit Client data.| The Fitbit Client credentials are invalid. The operation cannot be performed. | -|1429| system | Too Many Requests | Data request limit for user has expired. | Please wait a minimum of one hour and try make the operation again. | -|1500| any | Generic Error | The message from error. | The description from error. | +| code | reference | message | description | +|-----|-----|-----|-----| +|1011| Expired Access Token | Access token expired. | The access token has been expired and needs to be refreshed. | +|1012| Invalid Access Token | Access token invalid. | The access token is invalid. Please make a new Fitbit Auth Data request and try again. | +|1021| Invalid Refresh Token | Refresh token invalid. | The refresh token is invalid. Please make a new Fitbit Auth Data request and try again. | +|1401| Invalid Client Credentials | Invalid Fitbit Client data.| The Fitbit Client credentials are invalid. The operation cannot be performed. | +|1429| Too Many Requests | Data request limit for user has expired. | Please wait a minimum of one hour and try make the operation again. | +|1500| Generic Error | The message from error. | The description from error. | [//]: # (These are reference links used in the body of this note.) [license-image]: https://img.shields.io/badge/license-Apache%202-blue.svg diff --git a/package-lock.json b/package-lock.json index b948d32..8946d5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "data-sync-agent", - "version": "1.5.7", + "version": "1.5.8", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -14,17 +14,17 @@ } }, "@babel/core": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.3.tgz", - "integrity": "sha512-4XFkf8AwyrEG7Ziu3L2L0Cv+WyY47Tcsp70JFmpftbAA1K7YL/sgE9jh9HyNj08Y/U50ItUchpN0w6HxAoX1rA==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.4.tgz", + "integrity": "sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA==", "dev": true, "requires": { "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.3", - "@babel/helpers": "^7.8.3", - "@babel/parser": "^7.8.3", + "@babel/generator": "^7.8.4", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.4", "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.3", + "@babel/traverse": "^7.8.4", "@babel/types": "^7.8.3", "convert-source-map": "^1.7.0", "debug": "^4.1.0", @@ -60,9 +60,9 @@ } }, "@babel/generator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.3.tgz", - "integrity": "sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz", + "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==", "dev": true, "requires": { "@babel/types": "^7.8.3", @@ -101,13 +101,13 @@ } }, "@babel/helpers": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.3.tgz", - "integrity": "sha512-LmU3q9Pah/XyZU89QvBgGt+BCsTPoQa+73RxAQh8fb8qkDyIfeQnmgs+hvzhTCKTzqOyk7JTkS3MS1S8Mq5yrQ==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", "dev": true, "requires": { "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.3", + "@babel/traverse": "^7.8.4", "@babel/types": "^7.8.3" } }, @@ -123,9 +123,9 @@ } }, "@babel/parser": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", - "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", + "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==", "dev": true }, "@babel/template": { @@ -140,16 +140,16 @@ } }, "@babel/traverse": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.3.tgz", - "integrity": "sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz", + "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==", "dev": true, "requires": { "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.3", + "@babel/generator": "^7.8.4", "@babel/helper-function-name": "^7.8.3", "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.8.3", + "@babel/parser": "^7.8.4", "@babel/types": "^7.8.3", "debug": "^4.1.0", "globals": "^11.1.0", @@ -259,9 +259,9 @@ } }, "@sinonjs/commons": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.6.0.tgz", - "integrity": "sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.0.tgz", + "integrity": "sha512-qbk9AP+cZUsKdW1GJsBpxPKFmCJ0T8swwzVje3qFd+AkQb74Q/tiuzrdfFg8AD2g5HH/XbE/I8Uc1KYHVYWfhg==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -314,18 +314,18 @@ } }, "@types/bull": { - "version": "3.10.6", - "resolved": "https://registry.npmjs.org/@types/bull/-/bull-3.10.6.tgz", - "integrity": "sha512-koy8qpeBwTFSb/iZMoDPdCsXCWWRhVdGyzLAnqCHfOFAPE3PQJUZNEnVkv7xGYT9uXreTdlP0ucZTvgng2SEEg==", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/@types/bull/-/bull-3.12.0.tgz", + "integrity": "sha512-oKj9X8bxBF7OyAsCPGg2hu9msQPM/WwIRJfUHd0xzmKDMYOBepzbWdIuQDoX1xyvDskdjbW2Io7chbxqARae7A==", "dev": true, "requires": { "@types/ioredis": "*" } }, "@types/chai": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.7.tgz", - "integrity": "sha512-luq8meHGYwvky0O7u0eQZdA7B4Wd9owUCqvbw2m3XCrCU8mplYOujMBbvyS547AxJkC+pGnd0Cm15eNxEUNU8g==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.8.tgz", + "integrity": "sha512-U1bQiWbln41Yo6EeHMr+34aUhvrMVyrhn9lYfPSpLTCrZlGxU4Rtn1bocX+0p2Fc/Jkd2FanCEXdw0WNfHHM0w==", "dev": true }, "@types/color-name": { @@ -335,9 +335,9 @@ "dev": true }, "@types/connect": { - "version": "3.4.32", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", - "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", "dev": true, "requires": { "@types/node": "*" @@ -355,9 +355,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.0.tgz", - "integrity": "sha512-Xnub7w57uvcBqFdIGoRg1KhNOeEj0vB6ykUM7uFWyxvbdE89GFyqgmUcanAriMr4YOxNFZBAWkfcWIb4WBPt3g==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz", + "integrity": "sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg==", "dev": true, "requires": { "@types/node": "*", @@ -380,9 +380,9 @@ } }, "@types/ioredis": { - "version": "4.0.21", - "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.0.21.tgz", - "integrity": "sha512-IGMWaWsjPDh+aBx/HClsnUkalN+oPmSvUZxYUZVlOMPe9BZN8bi/2erTgSPDyIT52B9oHf/oSuW74OlmjJijkQ==", + "version": "4.14.6", + "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.14.6.tgz", + "integrity": "sha512-VUbEZaeCfdiqfd3UDtmPpwewCBdbnjpMZtarKuZV7XwkhqgBZN208WQpsD3hT0BJqEx3GPApFnIVnIOq/eBpbA==", "dev": true, "requires": { "@types/node": "*" @@ -436,9 +436,9 @@ } }, "@types/node": { - "version": "12.12.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.14.tgz", - "integrity": "sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA==", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.0.tgz", + "integrity": "sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==", "dev": true }, "@types/range-parser": { @@ -493,11 +493,11 @@ } }, "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" @@ -643,9 +643,9 @@ "dev": true }, "arg": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.2.tgz", - "integrity": "sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, "argparse": { @@ -853,9 +853,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz", - "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==" + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" }, "axios": { "version": "0.19.2", @@ -979,10 +979,7 @@ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "dev": true, - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - } + "optional": true }, "bitsyntax": { "version": "0.1.0", @@ -1017,9 +1014,9 @@ } }, "bowser": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.7.0.tgz", - "integrity": "sha512-aIlMvstvu8x+34KEiOHD3AsBgdrzg6sxALYiukOWhFvGMbQI6TRP/iY0LMhUrHs56aD6P1G0Z7h45PUJaa5m9w==" + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.9.0.tgz", + "integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==" }, "boxen": { "version": "1.3.0", @@ -1396,9 +1393,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -1560,9 +1557,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -1946,9 +1943,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -2032,9 +2029,9 @@ } }, "env-variable": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", - "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==" + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.6.tgz", + "integrity": "sha512-bHz59NlBbtS0NhftmR8+ExBEekE7br0e01jw+kk0NDro7TtZzBYZ5ScGPs3OmwnpyfHTHOtr1Y6uedCdrIldtg==" }, "error-ex": { "version": "1.3.2", @@ -2054,20 +2051,21 @@ } }, "es-abstract": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.3.tgz", - "integrity": "sha512-WtY7Fx5LiOnSYgF5eg/1T+GONaGmpvpPdCpSnYij+U2gDTL0UPfWrhDw7b2IYb+9NQJsYpCA0wOQvZfsd6YwRw==", + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", - "string.prototype.trimleft": "^2.1.0", - "string.prototype.trimright": "^2.1.0" + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" } }, "es-to-primitive": { @@ -2385,14 +2383,14 @@ } }, "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" }, "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=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "fast-safe-stringify": { "version": "2.0.7", @@ -2417,13 +2415,6 @@ "moment": "^2.11.2" } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -2540,6 +2531,14 @@ "dev": true, "requires": { "is-buffer": "~2.0.3" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", + "dev": true + } } }, "flush-write-stream": { @@ -2559,9 +2558,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3330,12 +3329,9 @@ "dev": true }, "get-port": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.0.0.tgz", - "integrity": "sha512-imzMU0FjsZqNa6BqOjbbW6w5BivHIuQKopjpPqcnx0AVHJQKCxK1O+Ab3OrVXhrekqfVMjwA9ZYu062R+KcIsQ==", - "requires": { - "type-fest": "^0.3.0" - } + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==" }, "get-stream": { "version": "3.0.0", @@ -3417,9 +3413,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3621,9 +3617,9 @@ "dev": true }, "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz", + "integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -3746,12 +3742,6 @@ "kind-of": "^4.0.0" }, "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -3778,12 +3768,6 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true } } }, @@ -3844,9 +3828,9 @@ "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==" }, "highlight.js": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.0.tgz", - "integrity": "sha512-A97kI1KAUzKoAiEoaGcf2O9YPS8nbDTCRFokaaeBhnqjQTvbAuAJrQMm21zw8s8xzaMtCQBtgbyGXLGxdxQyqQ==", + "version": "9.18.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz", + "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==", "dev": true }, "hoek": { @@ -4271,12 +4255,6 @@ "kind-of": "^3.0.2" }, "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -4303,15 +4281,15 @@ } }, "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" }, "is-ci": { "version": "1.2.1", @@ -4331,12 +4309,6 @@ "kind-of": "^3.0.2" }, "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -4349,9 +4321,9 @@ } }, "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" }, "is-descriptor": { "version": "0.1.6", @@ -4413,11 +4385,11 @@ } }, "is-nan": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.2.1.tgz", - "integrity": "sha1-n69ltvttskt/XAYoR16nH5iEAeI=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.0.tgz", + "integrity": "sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ==", "requires": { - "define-properties": "^1.1.1" + "define-properties": "^1.1.3" } }, "is-negated-glob": { @@ -4441,12 +4413,6 @@ "kind-of": "^3.0.2" }, "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -4489,11 +4455,11 @@ "dev": true }, "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", "requires": { - "has": "^1.0.1" + "has": "^1.0.3" } }, "is-relative": { @@ -4602,9 +4568,9 @@ } }, "istanbul-lib-instrument": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.0.tgz", - "integrity": "sha512-Nm4wVHdo7ZXSG30KjZ2Wl5SU/Bw7bDx1PdaiIFzEStdjs0H12mOTncn1GVYuqQSaZxpg87VGBRsVRPGD2cD1AQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", "dev": true, "requires": { "@babel/core": "^7.7.5", @@ -4923,9 +4889,9 @@ "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==" }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, "kuler": { @@ -4971,9 +4937,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -5315,16 +5281,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.42.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", - "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" }, "mime-types": { - "version": "2.1.25", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", - "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", "requires": { - "mime-db": "1.42.0" + "mime-db": "1.43.0" } }, "minimatch": { @@ -5731,18 +5697,27 @@ "dev": true }, "nise": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.2.tgz", - "integrity": "sha512-/6RhOUlicRCbE9s+94qCUsyE+pKlVJ5AhIv+jEE7ESKwnbXqulKZ1FYU+XAtHHWE9TinYvAxDUJAb912PwPoWA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", + "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", "dev": true, "requires": { "@sinonjs/formatio": "^3.2.1", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", - "lolex": "^4.1.0", + "lolex": "^5.0.1", "path-to-regexp": "^1.7.0" }, "dependencies": { + "lolex": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", + "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, "path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", @@ -6154,12 +6129,6 @@ "is-descriptor": "^0.1.0" } }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -6199,7 +6168,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, "requires": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", @@ -6220,12 +6188,12 @@ } }, "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" } }, "object.map": { @@ -6323,9 +6291,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -6363,9 +6331,9 @@ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -6676,12 +6644,12 @@ "dev": true }, "promise.prototype.finally": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/promise.prototype.finally/-/promise.prototype.finally-3.1.1.tgz", - "integrity": "sha512-gnt8tThx0heJoI3Ms8a/JdkYBVhYP/wv+T7yQimR+kdOEJL21xTFbiJhMRqnSPcr54UVvMbsscDk2w+ivyaLPw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/promise.prototype.finally/-/promise.prototype.finally-3.1.2.tgz", + "integrity": "sha512-A2HuJWl2opDH0EafgdjwEw7HysI8ff/n4lW4QEVBCUXFk9QeGecBWv0Deph0UmLe3tTNYegz8MOjsVuE6SMoJA==", "requires": { "define-properties": "^1.1.3", - "es-abstract": "^1.13.0", + "es-abstract": "^1.17.0-next.0", "function-bind": "^1.1.1" } }, @@ -6701,9 +6669,9 @@ "dev": true }, "psl": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.5.0.tgz", - "integrity": "sha512-4vqUjKi2huMu1OJiLhi3jN6jeeKvMZdI1tYgi/njW5zV52jNLgSAZSdN16m9bJFe61/cT8ulmw4qFitV9QRsEA==" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" }, "pstree.remy": { "version": "1.1.7", @@ -6830,9 +6798,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -6943,14 +6911,6 @@ "requires": { "is-buffer": "^1.1.5", "is-utf8": "^0.2.1" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - } } }, "remove-bom-stream": { @@ -7067,9 +7027,9 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, "resolve": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz", - "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz", + "integrity": "sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -7445,12 +7405,6 @@ "kind-of": "^3.2.0" }, "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -7469,12 +7423,12 @@ "dev": true }, "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", "dev": true, "requires": { - "atob": "^2.1.1", + "atob": "^2.1.2", "decode-uri-component": "^0.2.0", "resolve-url": "^0.2.1", "source-map-url": "^0.4.0", @@ -7660,9 +7614,9 @@ "dev": true }, "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, "string-width": { @@ -7677,18 +7631,18 @@ } }, "string.prototype.trimleft": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", - "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", "requires": { "define-properties": "^1.1.3", "function-bind": "^1.1.1" } }, "string.prototype.trimright": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", - "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", "requires": { "define-properties": "^1.1.3", "function-bind": "^1.1.1" @@ -7769,9 +7723,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -7884,9 +7838,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -7956,12 +7910,6 @@ "kind-of": "^3.0.2" }, "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -8144,9 +8092,10 @@ "dev": true }, "type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==" + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true }, "type-is": { "version": "1.6.18", @@ -8215,9 +8164,9 @@ "integrity": "sha512-kMBmblijHJXyOpKzgDhKx9INYU4u4E1RPMB0HqmKSgWG8vEcf3exEfLh4FFfzd3xdQOw9EuIy/cP0akY6rHopQ==" }, "uglify-js": { - "version": "3.7.6", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.6.tgz", - "integrity": "sha512-yYqjArOYSxvqeeiYH2VGjZOqq6SVmhxzaPjJC1W2F9e+bqvFL9QXQ2osQuKUFjM2hGjKG2YclQnRKWQSt/nOTQ==", + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.7.tgz", + "integrity": "sha512-FeSU+hi7ULYy6mn8PKio/tXsdSXN35lm4KgV2asx00kzrLU9Pi3oAslcJT70Jdj7PHX29gGUPOT6+lXGBbemhA==", "dev": true, "optional": true, "requires": { @@ -8440,12 +8389,14 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" } }, "utils-merge": { @@ -8454,9 +8405,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, "v8flags": { "version": "3.1.3", @@ -8544,9 +8495,9 @@ "dev": true }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -8667,9 +8618,9 @@ }, "dependencies": { "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz", + "integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -8717,9 +8668,9 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", diff --git a/package.json b/package.json index b04e9ad..5543b80 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-sync-agent", - "version": "1.5.7", + "version": "1.5.8", "description": "Service responsible for data synchronization of FitBit and CVE platform with OCARIoT platform.", "main": "dist/server.js", "scripts": { diff --git a/src/application/domain/model/resource.ts b/src/application/domain/model/resource.ts index 510f721..90adc22 100644 --- a/src/application/domain/model/resource.ts +++ b/src/application/domain/model/resource.ts @@ -4,6 +4,7 @@ import { IJSONDeserializable } from '../utils/json.deserializable.interface' import { JsonUtils } from '../utils/json.utils' export class Resource extends Entity implements IJSONSerializable, IJSONDeserializable { + private _type?: string private _resource?: any private _date_sync?: string private _user_id?: string @@ -13,6 +14,14 @@ export class Resource extends Entity implements IJSONSerializable, IJSONDeserial super() } + get type(): string | undefined { + return this._type + } + + set type(value: string | undefined) { + this._type = value + } + get resource(): any | undefined { return this._resource } @@ -51,7 +60,8 @@ export class Resource extends Entity implements IJSONSerializable, IJSONDeserial json = JSON.parse(json) } - if (json.id) super.id = json.id + if (json.id !== undefined) super.id = json.id + if (json.type !== undefined) this.type = json.type if (json.resource !== undefined) this.resource = json.resource if (json.user_id !== undefined) this.user_id = json.user_id if (json.date_sync !== undefined) this.date_sync = json.date_sync @@ -63,6 +73,7 @@ export class Resource extends Entity implements IJSONSerializable, IJSONDeserial public toJSON(): any { return { id: super.id, + type: this.type, resource: this.resource, user_id: this.user_id, date_sync: this.date_sync, diff --git a/src/application/domain/validator/resource.data.type.validator.ts b/src/application/domain/validator/resource.data.type.validator.ts deleted file mode 100644 index 009be3b..0000000 --- a/src/application/domain/validator/resource.data.type.validator.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ValidationException } from '../exception/validation.exception' -import { ResourceDataType } from '../utils/resource.data.type' - -export class ResourceDataTypeValidator { - public static validate(type: ResourceDataType): void | ValidationException { - if (!(Object.values(ResourceDataType).includes(type))) { - throw new ValidationException(`Value not mapped for resource data type: ${type}`, - `The mapped values are: ${Object.values(ResourceDataType).join(',')}.`) - } - } -} diff --git a/src/application/integration-event/handler/user.delete.event.handler.ts b/src/application/integration-event/handler/user.delete.event.handler.ts index d6cf818..10b9276 100644 --- a/src/application/integration-event/handler/user.delete.event.handler.ts +++ b/src/application/integration-event/handler/user.delete.event.handler.ts @@ -5,7 +5,6 @@ import { DIContainer } from '../../../di/di' import { ValidationException } from '../../domain/exception/validation.exception' import { IUserAuthDataRepository } from '../../port/user.auth.data.repository.interface' import { Query } from '../../../infrastructure/repository/query/query' -import { IFitbitDataRepository } from '../../port/fitbit.auth.data.repository.interface' import { UserAuthData } from '../../domain/model/user.auth.data' import { IResourceRepository } from '../../port/resource.repository.interface' @@ -19,8 +18,6 @@ export const userDeleteEventHandler = async (event: any) => { const logger: ILogger = DIContainer.get(Identifier.LOGGER) const userAuthDataRepo: IUserAuthDataRepository = DIContainer.get(Identifier.USER_AUTH_DATA_REPOSITORY) - const fitbitAuthDataRepo: IFitbitDataRepository = - DIContainer.get(Identifier.FITBIT_DATA_REPOSITORY) const resourceRepo: IResourceRepository = DIContainer.get(Identifier.RESOURCE_REPOSITORY) try { @@ -37,21 +34,6 @@ export const userDeleteEventHandler = async (event: any) => { const query: Query = new Query().fromJSON({ filters: { user_id: childId } }) const userAuthData: UserAuthData = await userAuthDataRepo.findOne(query) if (userAuthData) { - if (userAuthData.fitbit!.scope!) { - const payload: any = await fitbitAuthDataRepo.getTokenPayload(userAuthData.fitbit!.access_token!) - if (payload || payload.scopes) { - const scopes: Array = payload.scopes.split(' ') - if (scopes.includes('rwei')) { // Scope reference from fitbit to weight data is rwei - await fitbitAuthDataRepo.unsubscribeUserEvent(userAuthData.fitbit!, 'body', 'BODY') - } - if (scopes.includes('ract')) { // Scope reference from fitbit to activity data is ract - await fitbitAuthDataRepo.unsubscribeUserEvent(userAuthData.fitbit!, 'activities', 'ACTIVITIES') - } - if (scopes.includes('rsle')) { // Scope reference from fitbit to sleep data is rsle - await fitbitAuthDataRepo.unsubscribeUserEvent(userAuthData.fitbit!, 'sleep', 'SLEEP') - } - } - } await userAuthDataRepo.deleteByQuery(query) await resourceRepo.deleteByQuery(query) // 3. If got here, it's because the action was successful. diff --git a/src/application/port/fitbit.auth.data.repository.interface.ts b/src/application/port/fitbit.auth.data.repository.interface.ts index b5cbe4b..935697a 100644 --- a/src/application/port/fitbit.auth.data.repository.interface.ts +++ b/src/application/port/fitbit.auth.data.repository.interface.ts @@ -16,11 +16,5 @@ export interface IFitbitDataRepository { syncFitbitData(data: FitbitAuthData, userId: string): Promise - syncLastFitbitData(data: FitbitAuthData, userId: string, type: string, date: string): Promise - - subscribeUserEvent(data: FitbitAuthData, resource: string, subscriptionId: string): Promise - - unsubscribeUserEvent(data: FitbitAuthData, resource: string, subscriptionId: string): Promise - getTokenPayload(token: string): Promise } diff --git a/src/application/port/fitbit.client.repository.interface.ts b/src/application/port/fitbit.client.repository.interface.ts index dd2d8f9..300127a 100644 --- a/src/application/port/fitbit.client.repository.interface.ts +++ b/src/application/port/fitbit.client.repository.interface.ts @@ -1,13 +1,7 @@ -import { FitbitAuthData } from '../domain/model/fitbit.auth.data' - export interface IFitbitClientRepository { revokeToken(accessToken: string): Promise refreshToken(accessToken: string, refreshToken: string, expiresIn?: number): Promise getDataFromPath(path: string, accessToken: string): Promise - - subscribeUserEvent(data: FitbitAuthData, resource: string, subscriptionId: string): Promise - - unsubscribeUserEvent(data: FitbitAuthData, resource: string, subscriptionId: string): Promise } diff --git a/src/application/port/user.auth.data.service.interface.ts b/src/application/port/user.auth.data.service.interface.ts index f506b9b..9c1abd0 100644 --- a/src/application/port/user.auth.data.service.interface.ts +++ b/src/application/port/user.auth.data.service.interface.ts @@ -8,6 +8,4 @@ export interface IUserAuthDataService extends IService { revokeFitbitAccessToken(userId: string): Promise syncFitbitDataFromUser(userId: string): Promise - - syncLastFitbitUserData(fitbitUserId: string, type: string, date: string): Promise } diff --git a/src/application/service/user.auth.data.service.ts b/src/application/service/user.auth.data.service.ts index 94056cb..d944076 100644 --- a/src/application/service/user.auth.data.service.ts +++ b/src/application/service/user.auth.data.service.ts @@ -30,10 +30,9 @@ export class UserAuthDataService implements IUserAuthDataService { try { const authData: UserAuthData = await this.manageFitbitAuthData(item) CreateUserAuthDataValidator.validate(item) - let result: UserAuthData = new UserAuthData() + let result: UserAuthData authData.fitbit!.status = 'valid_token' - await this.subscribeFitbitEvents(item) const alreadySaved: UserAuthData = await this._userAuthDataRepo .findOne(new Query().fromJSON({ filters: { user_id: authData.user_id! } })) @@ -45,7 +44,10 @@ export class UserAuthDataService implements IUserAuthDataService { } if (authData.fitbit && authData.fitbit.last_sync) { - this._eventBus.bus.pubFitbitLastSync({ child_id: authData.user_id, last_sync: authData.fitbit.last_sync }) + this._eventBus.bus.pubFitbitLastSync({ + child_id: authData.user_id, + last_sync: authData.fitbit.last_sync + }) .then(() => this._logger.info(`Last sync from ${authData.user_id} successful published!`)) .catch(err => this._logger.error(`Error at publish last sync: ${err.message}`)) } @@ -94,16 +96,13 @@ export class UserAuthDataService implements IUserAuthDataService { return resolve() } - // 2. Unsubscribe from Fitbit events. - await this.unsubscribeFitbitEvents(authData) - - // 3. Revokes Fitbit access token. - + // 2. Revokes Fitbit access token. const isRevoked: boolean = await this._fitbitAuthDataRepo.revokeToken(authData.fitbit.access_token) - // 4. Remove Fitbit authorization data from local database. + + // 3. Remove Fitbit authorization data from local database. const isRemoved: boolean = await this._fitbitAuthDataRepo.removeFitbitAuthData(userId) - // 5. Publish the Fitbit revoke event on the bus. + // 4. Publish the Fitbit revoke event on the bus. if (isRevoked && isRemoved) { this._eventBus.bus .pubFitbitRevoke({ child_id: userId }) @@ -165,19 +164,20 @@ export class UserAuthDataService implements IUserAuthDataService { }).catch(err => { if (err.type !== 'system') this.updateTokenStatus(userId, err.type) this.publishFitbitAuthError(err, userId) - return reject(err) + return reject(this.manageFitbitAuthError(err)) }) } else if (err.type === 'client_error') { try { const result: DataSync = await this.syncFitbitData(data.fitbit, userId) return resolve(result) } catch (err) { + if (err.type) return reject(this.manageFitbitAuthError(err)) return reject(err) } } else { if (err.type !== 'system') this.updateTokenStatus(userId, err.type) this.publishFitbitAuthError(err, userId) - return reject(err) + return reject(this.manageFitbitAuthError(err)) } } else { return reject(err) @@ -191,107 +191,6 @@ export class UserAuthDataService implements IUserAuthDataService { }) } - public async syncLastFitbitUserData(fitbitUserId: string, type: string, date: string): Promise { - try { - const authData: UserAuthData = - await this._userAuthDataRepo - .findOne(new Query().fromJSON({ filters: { 'fitbit.user_id': fitbitUserId } })) - if (!authData) return await Promise.resolve() - this.syncLastFitbitData(authData.fitbit!, authData.user_id!, type, date) - .then() - .catch(err => this._logger.error(`The resource ${type} from ${authData.user_id} could note be sync: ` + - err.message)) - return await Promise.resolve() - } catch (err) { - return await Promise.reject(err) - } - } - - private async syncLastFitbitData(data: FitbitAuthData, userId: string, type: string, date: string): Promise { - try { - VerifyFitbitAuthValidator.validate(data) - await this._fitbitAuthDataRepo.syncLastFitbitData(data, userId, type, date) - return Promise.resolve() - } catch (err) { - if (err.type) { - if (err.type === 'expired_token') { - try { - const newToken: FitbitAuthData = - await this._fitbitAuthDataRepo.refreshToken(userId, data.access_token!, data.refresh_token!) - this.syncLastFitbitData(newToken, userId, type, date) - .then() - .catch(err => { - this.updateTokenStatus(userId, err.type) - this.publishFitbitAuthError(err, userId) - return Promise.reject(err) - }) - } catch (err) { - if (err.type !== 'system') this.updateTokenStatus(userId, err.type) - this.publishFitbitAuthError(err, userId) - return Promise.reject(err) - } - } else if (err.type === 'client_error') { - try { - await this.syncFitbitData(data, userId) - } catch (err) { - return Promise.reject(err) - } - } else { - if (err.type !== 'system') this.updateTokenStatus(userId, err.type) - this.publishFitbitAuthError(err, userId) - return Promise.reject(err) - } - } else { - return await Promise.reject(err) - } - } - } - - private async subscribeFitbitEvents(data: UserAuthData): Promise { - return new Promise(async (resolve, reject) => { - try { - if (!data || !data.fitbit || !data.fitbit.scope) return - - const scopes: Array = data.fitbit.scope.split(' ') - - if (scopes.includes('rwei')) { // Scope reference from fitbit to weight data is rwei - await this._fitbitAuthDataRepo.subscribeUserEvent(data.fitbit!, 'body', 'BODY') - } - if (scopes.includes('ract')) { // Scope reference from fitbit to activity data is ract - await this._fitbitAuthDataRepo.subscribeUserEvent(data.fitbit!, 'activities', 'ACTIVITIES') - } - if (scopes.includes('rsle')) { // Scope reference from fitbit to sleep data is rsle - await this._fitbitAuthDataRepo.subscribeUserEvent(data.fitbit!, 'sleep', 'SLEEP') - } - return resolve() - } catch (err) { - return reject(err) - } - }) - } - - private async unsubscribeFitbitEvents(data: UserAuthData): Promise { - return new Promise(async (resolve, reject) => { - try { - if (!data || !data.fitbit || !data.fitbit.scope) return - - const scopes: Array = data.fitbit.scope.split(' ') - if (scopes.includes('rwei')) { // Scope reference from fitbit to weight data is rwei - await this._fitbitAuthDataRepo.unsubscribeUserEvent(data.fitbit!, 'body', 'BODY') - } - if (scopes.includes('ract')) { // Scope reference from fitbit to activity data is ract - await this._fitbitAuthDataRepo.unsubscribeUserEvent(data.fitbit!, 'activities', 'ACTIVITIES') - } - if (scopes.includes('rsle')) { // Scope reference from fitbit to sleep data is rsle - await this._fitbitAuthDataRepo.unsubscribeUserEvent(data.fitbit!, 'sleep', 'SLEEP') - } - return resolve() - } catch (err) { - return reject(err) - } - }) - } - private async manageFitbitAuthData(data: UserAuthData): Promise { return new Promise(async (resolve, reject) => { try { @@ -330,33 +229,36 @@ export class UserAuthDataService implements IUserAuthDataService { private publishFitbitAuthError(error: any, userId: string): void { const fitbit: any = { child_id: userId, - error: { code: 0, message: error.message, description: error.description } + error: this.manageFitbitAuthError(error) } + this._logger.error(`Fitbit error: ${JSON.stringify(fitbit)}`) + this._eventBus.bus.pubFitbitAuthError(fitbit) + .then(() => this._logger.info(`Error message about ${error.type} from ${userId} successful published!`)) + .catch(err => this._logger.error(`Error at publish error message from ${userId}: ${err.message}`)) + } + private manageFitbitAuthError(error: any): any { + const result: any = { code: 0, message: error.message, description: error.description } switch (error.type) { case 'expired_token': - fitbit.error.code = 1011 + result.code = 1011 // 400 break case 'invalid_token': - fitbit.error.code = 1012 + result.code = 1012 // 400 break case 'invalid_grant': - fitbit.error.code = 1021 + result.code = 1021 // 400 break - case 'invalid_client': - fitbit.error.code = 1401 + case 'invalid_client': // 403 + result.code = 1401 break case 'system': - fitbit.error.code = 1429 + result.code = 1429 // 429 break default: - fitbit.error.code = 1500 + result.code = 1500 // 500 break } - - this._logger.error(`Fitbit error: ${JSON.stringify(fitbit)}`) - this._eventBus.bus.pubFitbitAuthError(fitbit) - .then(() => this._logger.info(`Error message about ${error.type} from ${userId} successful published!`)) - .catch(err => this._logger.error(`Error at publish error message from ${userId}: ${err.message}`)) + return result } } diff --git a/src/di/di.ts b/src/di/di.ts index eb152ef..b8732e3 100644 --- a/src/di/di.ts +++ b/src/di/di.ts @@ -17,7 +17,6 @@ import { IEntityMapper } from '../infrastructure/port/entity.mapper.interface' import { UserAuthDataService } from '../application/service/user.auth.data.service' import { IBackgroundTask } from '../application/port/background.task.interface' import { SyncFitbitDataTask } from '../background/task/sync.fitbit.data.task' -import { FitbitSubscriberController } from '../ui/controllers/fitbit.subscriber.controller' import { FitbitAuthDataEntityMapper } from '../infrastructure/entity/mapper/fitbit.auth.data.entity.mapper' import { UserAuthData } from '../application/domain/model/user.auth.data' import { UserAuthDataEntity } from '../infrastructure/entity/user.auth.data.entity' @@ -76,8 +75,6 @@ class IoC { // Controllers this._container.bind(Identifier.HOME_CONTROLLER).to(HomeController).inSingletonScope() this._container.bind(Identifier.FITBIT_CONTROLLER).to(FitbitController).inSingletonScope() - this._container.bind(Identifier.FITBIT_SUBSCRIBER_CONTROLLER) - .to(FitbitSubscriberController).inSingletonScope() this._container.bind(Identifier.USER_FITBIT_AUTH_CONTROLLER) .to(UserFitbitAuthController).inSingletonScope() this._container.bind(Identifier.USER_FITBIT_SYNC_CONTROLLER) diff --git a/src/di/identifiers.ts b/src/di/identifiers.ts index 450f509..af3bc41 100644 --- a/src/di/identifiers.ts +++ b/src/di/identifiers.ts @@ -8,7 +8,6 @@ export abstract class Identifier { // Controllers public static readonly HOME_CONTROLLER: any = Symbol.for('HomeController') - public static readonly FITBIT_SUBSCRIBER_CONTROLLER: any = Symbol.for('FitbitSubscriberController') public static readonly FITBIT_CONTROLLER: any = Symbol.for('FitbitController') public static readonly USER_FITBIT_AUTH_CONTROLLER: any = Symbol.for('UserFitbitAuthController') public static readonly USER_FITBIT_SYNC_CONTROLLER: any = Symbol.for('UserFitbitSyncController') diff --git a/src/infrastructure/database/schema/resource.schema.ts b/src/infrastructure/database/schema/resource.schema.ts index bd5a3db..8d83160 100644 --- a/src/infrastructure/database/schema/resource.schema.ts +++ b/src/infrastructure/database/schema/resource.schema.ts @@ -4,10 +4,11 @@ interface IResourceSchemaModel extends Mongoose.Document { } const resourceSchema = new Mongoose.Schema({ - user_id: { type: String }, - resource: { type: Object }, - date_sync: { type: String }, - provider: { type: String } + user_id: String, + type: String, + resource: Object, + date_sync: String, + provider: String }, { strict: false, diff --git a/src/infrastructure/entity/mapper/resource.entity.mapper.ts b/src/infrastructure/entity/mapper/resource.entity.mapper.ts index 3f8dd90..f5738b8 100644 --- a/src/infrastructure/entity/mapper/resource.entity.mapper.ts +++ b/src/infrastructure/entity/mapper/resource.entity.mapper.ts @@ -14,6 +14,7 @@ export class ResourceEntityMapper implements IEntityMapper { - return new Promise((resolve, reject) => { - this.fitbit_client - .post( - `/${resource}/apiSubscriptions/${subscriptionId}-${data.user_id}.json`, // Path - data.access_token, // Access Token - null, // Form Data - null, // User Id - { 'X-Fitbit-Subscriber-Id': process.env.FITBIT_SUBSCRIBER_ID } // Extra Header - ) - .then(res => { - if (res[0].errors) { - return reject(this.fitbitClientErrorListener(res[0].errors[0])) - } - return resolve() - }).catch(err => reject(this.fitbitClientErrorListener(err))) - }) - } - - public async unsubscribeUserEvent(data: FitbitAuthData, resource: string, subscriptionId: string): Promise { - return new Promise((resolve, reject) => { - this.fitbit_client - .delete( - `/${resource}/apiSubscriptions/${subscriptionId}-${data.user_id}.json`, // Path - data.access_token, // Access Token - null, // User Id - { 'X-Fitbit-Subscriber-Id': process.env.FITBIT_SUBSCRIBER_ID } // Extra Header - ) - .then(res => { - if (res[0] && res[0].errors) { - return reject(this.fitbitClientErrorListener(res[0].errors[0])) - } - return resolve() - }).catch(err => reject(this.fitbitClientErrorListener(err))) - }) - } - - private fitbitClientErrorListener(err: any): OAuthException | FitbitClientException | undefined { - if (err.context) return new OAuthException(err.context.errors[0].errorType, err.context.errors[0].message) + private fitbitClientErrorListener(err: any): FitbitClientException | undefined { + if (err.context) return new FitbitClientException(err.context.errors[0].errorType, err.context.errors[0].message) else if (err.code === 'EAI_AGAIN') return new FitbitClientException('client_error', err.message) - return new OAuthException(err.errorType, err.message) + return new FitbitClientException(err.errorType, err.message) } } diff --git a/src/infrastructure/repository/fitbit.data.repository.ts b/src/infrastructure/repository/fitbit.data.repository.ts index 4080fd0..a544977 100644 --- a/src/infrastructure/repository/fitbit.data.repository.ts +++ b/src/infrastructure/repository/fitbit.data.repository.ts @@ -3,7 +3,6 @@ import { Identifier } from '../../di/identifiers' import { ILogger } from '../../utils/custom.logger' import { IEntityMapper } from '../port/entity.mapper.interface' import { FitbitAuthData } from '../../application/domain/model/fitbit.auth.data' -import { OAuthException } from '../../application/domain/exception/oauth.exception' import moment from 'moment' import jwt from 'jsonwebtoken' import { PhysicalActivity } from '../../application/domain/model/physical.activity' @@ -93,26 +92,6 @@ export class FitbitDataRepository implements IFitbitDataRepository { } } - public async subscribeUserEvent(data: FitbitAuthData, resource: string, subscriptionId: string): Promise { - try { - await this._fitbitClientRepo.subscribeUserEvent(data, resource, subscriptionId) - this._logger.info(`Successful subscribe on ${resource} resource from user ${data.user_id}.`) - return Promise.resolve() - } catch (err) { - return Promise.reject(this.fitbitClientErrorListener(err, data.access_token!, data.refresh_token!)) - } - } - - public async unsubscribeUserEvent(data: FitbitAuthData, resource: string, subscriptionId: string): Promise { - try { - await this._fitbitClientRepo.unsubscribeUserEvent(data, resource, subscriptionId) - this._logger.info(`Successful unsubscribe on ${resource} resource from user ${data.user_id}.`) - return Promise.resolve() - } catch (err) { - return Promise.reject(this.fitbitClientErrorListener(err, data.access_token!, data.refresh_token!)) - } - } - public async syncFitbitData(data: FitbitAuthData, userId: string): Promise { return new Promise(async (resolve, reject) => { try { @@ -135,12 +114,12 @@ export class FitbitDataRepository implements IFitbitDataRepository { if (scopes.includes('rsle')) syncSleep = await this.syncSleepData(data) if (scopes.includes('ract')) { promises.push(this.syncUserActivities(data)) - promises.push(this.syncUserActivitiesLogs(data, data.last_sync!, 'steps')) - promises.push(this.syncUserActivitiesLogs(data, data.last_sync!, 'calories')) - promises.push(this.syncUserActivitiesLogs(data, data.last_sync!, 'minutesSedentary')) - promises.push(this.syncUserActivitiesLogs(data, data.last_sync!, 'minutesLightlyActive')) - promises.push(this.syncUserActivitiesLogs(data, data.last_sync!, 'minutesFairlyActive')) - promises.push(this.syncUserActivitiesLogs(data, data.last_sync!, 'minutesVeryActive')) + promises.push(this.syncUserActivitiesLogs(data, 'steps')) + promises.push(this.syncUserActivitiesLogs(data, 'calories')) + promises.push(this.syncUserActivitiesLogs(data, 'minutesSedentary')) + promises.push(this.syncUserActivitiesLogs(data, 'minutesLightlyActive')) + promises.push(this.syncUserActivitiesLogs(data, 'minutesFairlyActive')) + promises.push(this.syncUserActivitiesLogs(data, 'minutesVeryActive')) } const result = await Promise.all(promises) syncActivities = result[0] || [] @@ -173,45 +152,33 @@ export class FitbitDataRepository implements IFitbitDataRepository { if (activitiesList.length) { this._eventBus.bus .pubSyncPhysicalActivity(activitiesList.map(item => item.toJSON())) - .then(() => { - this._logger.info(`Physical activities from ${userId} successful published!`) - this.saveResourceList(activities, userId) - .then(() => this._logger.info(`Physical Activity logs from ${userId} saved successful!`)) - .catch(err => this._logger.error(`Error at save physical activities logs: ${err.message}`)) - }) + .then(() => this._logger.info(`Physical activities from ${userId} successful published!`)) .catch(err => this._logger.error(`Error publishing physical activities: ${err.message}`)) } + if (weightList.length) { this._eventBus.bus .pubSyncWeight(weightList.map(item => item.toJSON())) - .then(() => { - this._logger.info(`Weight Measurements from ${userId} successful published!`) - this.saveResourceList(weights, userId) - .then(() => this._logger.info(`Weight logs from ${data.user_id} saved successful!`)) - .catch(err => this._logger.error(`Error at save weight logs: ${err.message}`)) - }) + .then(() => this._logger.info(`Weight Measurements from ${userId} successful published!`)) .catch(err => this._logger.error(`Error publishing weights: ${err.message}`)) } if (sleepList.length) { this._eventBus.bus .pubSyncSleep(sleepList.map(item => item.toJSON())) - .then(() => { - this._logger.info(`Sleep from ${userId} successful published!`) - this.saveResourceList(sleep, userId) - .then(() => this._logger.info(`Sleep logs from ${userId} saved successful!`)) - .catch(err => this._logger.error(`Error at save sleep logs: ${err.message}`)) - }) + .then(() => this._logger.info(`Sleep from ${userId} successful published!`)) .catch(err => this._logger.error(`Error publishing sleep: ${err.message}`)) } + this.manageResources(syncActivities, userId, ResourceDataType.ACTIVITIES) + this.manageResources(syncWeights, userId, ResourceDataType.BODY) + this.manageResources(syncSleep, userId, ResourceDataType.SLEEP) + const logList: Array = userLog.toJSONList() if (logList && logList.length) { this._eventBus.bus .pubSyncLog(logList) - .then(() => { - this._logger.info(`Activities logs from ${userId} successful published!`) - }) + .then(() => this._logger.info(`Activities logs from ${userId} successful published!`)) .catch(err => this._logger.error(`Error publishing logs: ${err.message}`)) } @@ -254,24 +221,6 @@ export class FitbitDataRepository implements IFitbitDataRepository { }) } - public async syncLastFitbitData(data: FitbitAuthData, userId: string, type: string, date: string): Promise { - try { - if (type === ResourceDataType.BODY) await this.syncLastFitbitUserWeight(data, userId, date) - else if (type === ResourceDataType.ACTIVITIES) { - await this.syncLastFitbitUserActivity(data, userId, date) - await this.syncLastFitbitUserActivityLogs(data, userId, date) - } else if (type === ResourceDataType.SLEEP) await this.syncLastFitbitUserSleep(data, userId, date) - const lastSync: string = moment.utc().format() - this.updateLastSync(userId, lastSync) - .then(res => { - if (res) this.publishLastSync(userId, lastSync) - }).catch(err => this._logger.info(`Error at update the last sync: ${err.message}`)) - return Promise.resolve() - } catch (err) { - return Promise.reject(err) - } - } - public getTokenPayload(token: string): Promise { try { return Promise.resolve(jwt.decode(token)) @@ -306,8 +255,9 @@ export class FitbitDataRepository implements IFitbitDataRepository { const resources: Array = [] if (!data || !data.length) return resources for await(const item of data) { - const query: Query = new Query().fromJSON({ filters: { 'resource.logId': item.logId, 'user_id': userId } }) - if (type === ResourceDataType.BODY) query.addFilter({ 'resource.weight': item.weight }) + const query: Query = new Query().fromJSON({ + filters: { 'resource.logId': item.logId, user_id: userId, type } + }) const exists: boolean = await this._resourceRepo.checkExists(query) if (!exists) resources.push(item) } @@ -317,136 +267,32 @@ export class FitbitDataRepository implements IFitbitDataRepository { } } - private syncLastFitbitUserWeight(data: FitbitAuthData, userId: string, date: string): Promise { - return new Promise((resolve, reject) => { - this.getUserBodyDataFromInterval(data.access_token!, date, date) - .then(async weights => { - if (weights && weights.length) { - const resources: Array = await this.filterDataAlreadySync(weights, ResourceDataType.BODY, userId) - - // Parse list of weights - const weightList: Array = this.parseWeightList(resources, userId) - if (weightList.length) { - // Publish list of weights - this._eventBus.bus.pubSyncWeight(weightList.map(item => item.toJSON())) - .then(() => { - this._logger.info(`Weight Measurements from ${userId} successful published!`) - this.saveResourceList(resources, userId) - .then(() => { - // If publish is successful, save the sync resources on database - this._logger.info(`Weight logs from ${userId} saved successful!`) - }) - .catch(err => { - this._logger.error(`Error at save weight: ${err.message}`) - }) - }) - .catch(err => this._logger.error(`Error at publish weight: ${err.message}`)) - } - } - return resolve() - }).catch(err => reject(err)) - }) - } - - private syncLastFitbitUserActivity(data: FitbitAuthData, userId: string, date: string): Promise { - return new Promise((resolve, reject) => { - this.getUserActivities(data.access_token!, 100, date) - .then(async activities => { - if (activities && activities.length) { - const resources: Array = - await this.filterDataAlreadySync(activities, ResourceDataType.ACTIVITIES, userId) - - // Parse list of activities - const activityList: Array = this.parsePhysicalActivityList(resources, userId) - if (activityList.length) { - // Publish list of activities - this._eventBus.bus.pubSyncPhysicalActivity(activityList.map(item => item.toJSON())) - .then(() => { - this._logger.info(`Physical activities from ${userId} successful published!`) - this.saveResourceList(resources, userId) - .then(() => { - // If publish is successful, save the sync resources on database - this._logger.info(`Physical activities from ${userId} saved successful!`) - }) - .catch(err => { - this._logger.error(`Error at save physical activities: ${err.message}`) - }) - }) - .catch(err => this._logger.error(`Error at publish physical activities: ${err.message}`)) - } - } - return resolve() - }).catch(err => reject(err)) + private cleanResourceList(userId, type): Promise { + return new Promise((resolve, reject) => { + this._resourceRepo + .deleteByQuery(new Query().fromJSON({ filters: { user_id: userId, type } })) + .then(res => resolve(!!res)) + .catch(err => reject(this.mongoDBErrorListener(err))) }) } - private async syncLastFitbitUserActivityLogs(data: FitbitAuthData, userId: string, date: string): Promise { - try { - const stepsLogs: Array = await this.syncUserActivitiesLogs(data, date, 'steps') - const caloriesLogs: Array = await this.syncUserActivitiesLogs(data, date, 'calories') - const minutesSedentaryLogs: Array = await this.syncUserActivitiesLogs(data, date, 'minutesSedentary') - const minutesLightlyActiveLogs: Array = await this.syncUserActivitiesLogs(data, date, 'minutesLightlyActive') - const minutesFairlyActiveLogs: Array = await this.syncUserActivitiesLogs(data, date, 'minutesFairlyActive') - const minutesVeryActiveLogs: Array = await this.syncUserActivitiesLogs(data, date, 'minutesVeryActive') - - const userLog: UserLog = await this.parseActivityLogs( - stepsLogs, - caloriesLogs, - minutesSedentaryLogs, - minutesLightlyActiveLogs, - this.mergeLogsValues(minutesFairlyActiveLogs, minutesVeryActiveLogs), - userId - ) - - this._eventBus.bus.pubSyncLog(userLog.toJSONList()) - .then(() => { - this._logger.info(`Activities logs from ${userId} successful published!`) - }) - .catch(err => this._logger.error(`Error at publish activities logs: ${err.message}`)) - return Promise.resolve() - } catch (err) { - return Promise.reject(err) - } - } - - private syncLastFitbitUserSleep(data: FitbitAuthData, userId: string, date: string): Promise { - return new Promise((resolve, reject) => { - this.getUserSleepFromInterval(data.access_token!, date, date) - .then(async sleeps => { - if (sleeps && sleeps.length) { - const resources: Array = await this.filterDataAlreadySync(sleeps, ResourceDataType.SLEEP, userId) - - // Parse list of sleep - const sleepList: Array = this.parseSleepList(resources, userId) - if (sleepList.length) { - // Publish list of sleep. - this._eventBus.bus.pubSyncSleep(sleepList.map(item => item.toJSON())) - .then(() => { - this._logger.info(`Sleep from ${userId} successful published!`) - this.saveResourceList(resources, userId) - .then(() => { - // If publish is successful, save the sync resources on database - this._logger.info(`Sleep logs from ${userId} saved successful!`) - }) - .catch(err => { - this._logger.error(`Error at save sleep: ${err.message}`) - }) - }) - .catch(err => this._logger.error(`Error at publish sleep: ${err.message}`)) - } - } - return resolve() - }).catch(err => reject(err)) - }) + private manageResources(resources: Array, userId: string, type: string): void { + this.cleanResourceList(userId, type) + .then(() => { + this.saveResourceList(resources, userId, type) + .catch(err => this._logger.error(`Error at save physical activities logs: ${err.message}`)) + }) + .catch(err => this._logger.error(`Error at save physical activities logs: ${err.message}`)) } - private saveResourceList(resources: Array, userId: string): Promise> { + private saveResourceList(resources: Array, userId: string, type: string): Promise> { return new Promise>(async (resolve, reject) => { const result: Array = [] if (!resources || !resources.length) return result try { for await (const item of resources) { const resource: Resource = await this._resourceRepo.create(new Resource().fromJSON({ + type, resource: item, date_sync: moment().utc().format(), user_id: userId, @@ -464,13 +310,6 @@ export class FitbitDataRepository implements IFitbitDataRepository { private syncWeightData(data: FitbitAuthData): Promise> { return new Promise>(async (resolve, reject) => { try { - if ((data.last_sync && moment().diff(moment(data.last_sync), 'days') <= 31)) { - return resolve(await this.getUserBodyDataFromInterval( - data.access_token!, - moment(data.last_sync).format('YYYY-MM-DD'), - moment().format('YYYY-MM-DD'))) - } - const result: Array = new Array() result.push( this.getUserBodyDataFromInterval( @@ -493,17 +332,9 @@ export class FitbitDataRepository implements IFitbitDataRepository { }) } - // @ts-ignore private async syncSleepData(data: FitbitAuthData): Promise> { return new Promise>(async (resolve, reject) => { try { - if ((data.last_sync && moment().diff(moment(data.last_sync), 'days') <= 31)) { - return resolve(await this.getUserSleepAfter( - data.access_token!, - 100, - moment(data.last_sync).format('YYYY-MM-DD')) - ) - } return resolve(await this.getUserSleepBefore( data.access_token!, 100, @@ -515,34 +346,18 @@ export class FitbitDataRepository implements IFitbitDataRepository { } private syncUserActivities(data: FitbitAuthData): Promise> { - if (data.last_sync) { - return this.getUserActivities( - data.access_token!, 100, - moment(data.last_sync).format('YYYY-MM-DD') - ) - } return this.getLastUserActivities(data.access_token!) } - private async syncUserActivitiesLogs(data: FitbitAuthData, lastSync: string, resource: string): Promise> { + private async syncUserActivitiesLogs(data: FitbitAuthData, resource: string): Promise> { return this.getUserActivityLogs( data.access_token!, resource, - lastSync ? moment(lastSync).format('YYYY-MM-DD') : - moment().subtract(12, 'month').format('YYYY-MM-DD'), + moment().subtract(12, 'month').format('YYYY-MM-DD'), 'today' ) } - private async getUserSleepAfter(token: string, limit: number, afterDate: string): Promise { - const path: string = `/sleep/list.json?afterDate=${afterDate}&sort=desc&offset=0&limit=${limit}` - return new Promise((resolve, reject) => { - this._fitbitClientRepo.getDataFromPath(path, token) - .then(result => resolve(result.sleep)) - .catch(err => reject(this.fitbitClientErrorListener(err, token))) - }) - } - private async getUserSleepBefore(token: string, limit: number, beforeDate: string): Promise { const path: string = `/sleep/list.json?beforeDate=${beforeDate}&sort=desc&offset=0&limit=${limit}` return new Promise((resolve, reject) => { @@ -552,15 +367,6 @@ export class FitbitDataRepository implements IFitbitDataRepository { }) } - private async getUserSleepFromInterval(token: string, baseDate: string, endDate: string): Promise { - const path: string = `/sleep/date/${baseDate}/${endDate}.json` - return new Promise((resolve, reject) => { - this._fitbitClientRepo.getDataFromPath(path, token) - .then(result => resolve(result.sleep)) - .catch(err => reject(this.fitbitClientErrorListener(err, token))) - }) - } - private async getUserActivityLogs(token: string, resource: string, baseDate: string, endDate: string): Promise { return new Promise((resolve, reject) => { return this._fitbitClientRepo @@ -589,15 +395,6 @@ export class FitbitDataRepository implements IFitbitDataRepository { }) } - private async getUserActivities(token: string, limit: number, afterDate: string): Promise { - const path: string = `/activities/list.json?afterDate=${afterDate}&sort=desc&offset=0&limit=${limit}` - return new Promise((resolve, reject) => { - this._fitbitClientRepo.getDataFromPath(path, token) - .then(result => resolve(result.activities)) - .catch(err => reject(this.fitbitClientErrorListener(err, token))) - }) - } - private getSleepSummary(summary: any): any { if (summary.asleep && summary.awake && summary.restless) { return { @@ -664,12 +461,22 @@ export class FitbitDataRepository implements IFitbitDataRepository { const cardio = item.heartRateZones.find(zone => zone.name === 'Cardio') const peak = item.heartRateZones.find(zone => zone.name === 'Peak') - const out_of_range_zone = { min: out_of_range.min, max: out_of_range.max, duration: out_of_range.minutes * 60000 } + const out_of_range_zone = { + min: out_of_range.min, + max: out_of_range.max, + duration: out_of_range.minutes * 60000 + } const fat_burn_zone = { min: fat_burn.min, max: fat_burn.max, duration: fat_burn.minutes * 60000 } const cardio_zone = { min: cardio.min, max: cardio.max, duration: cardio.minutes * 60000 } const peak_zone = { min: peak.min, max: peak.max, duration: peak.minutes * 60000 } - activity.heart_rate = { average: item.averageHeartRate, out_of_range_zone, fat_burn_zone, cardio_zone, peak_zone } + activity.heart_rate = { + average: item.averageHeartRate, + out_of_range_zone, + fat_burn_zone, + cardio_zone, + peak_zone + } } return new PhysicalActivity().fromJSON(activity) @@ -765,8 +572,7 @@ export class FitbitDataRepository implements IFitbitDataRepository { }) } - private fitbitClientErrorListener(err: any, accessToken?: string, refreshToken?: string, userId?: string): - OAuthException | FitbitClientException | undefined { + private fitbitClientErrorListener(err: any, accessToken?: string, refreshToken?: string): FitbitClientException | undefined { if (err.type === 'client_error') { return new FitbitClientException( 'client_error', @@ -774,34 +580,34 @@ export class FitbitDataRepository implements IFitbitDataRepository { 'Please try again later.') } if (err.type === 'expired_token') { - return new OAuthException( + return new FitbitClientException( 'expired_token', 'Access token expired.', `The access token ${accessToken} has been expired and needs to be refreshed.`) } else if (err.type === 'invalid_token') { - return new OAuthException( + return new FitbitClientException( 'invalid_token', 'Access token invalid.', `The access token ${accessToken} is invalid. Please make a new Fitbit Auth Data request and try again.`) } else if (err.type === 'invalid_grant') { - return new OAuthException( + return new FitbitClientException( 'invalid_grant', 'Refresh token invalid.', `The refresh token ${refreshToken} is invalid. Please make a new Fitbit Auth Data request and try again.`) } else if (err.type === 'system') { - return new OAuthException( + return new FitbitClientException( 'system', `Data request limit for access token ${accessToken} has expired.`, 'Please wait a minimum of one hour and try make the operation again.') } else if (err.type === 'invalid_client') { - return new OAuthException( + return new FitbitClientException( 'invalid_client', 'Invalid Fitbit Client data.', 'The Fitbit Client credentials are invalid. The operation cannot be performed.') } else if (err.type === 'internal_error') { - return new OAuthException('internal_error', 'A internal error occurs. Please, try again later.') + return new FitbitClientException('internal_error', 'internal_fitbit_error', err.message) } - return new OAuthException(err.type, err.message) + return new FitbitClientException(err.type, err.message) } // MongoDb Error Listener diff --git a/src/ui/controllers/fitbit.subscriber.controller.ts b/src/ui/controllers/fitbit.subscriber.controller.ts deleted file mode 100644 index b508153..0000000 --- a/src/ui/controllers/fitbit.subscriber.controller.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { controller, httpGet, httpPost, request, response } from 'inversify-express-utils' -import { Request, Response } from 'express' -import HttpStatus from 'http-status-codes' -import { inject } from 'inversify' -import { Identifier } from '../../di/identifiers' -import { IUserAuthDataService } from '../../application/port/user.auth.data.service.interface' -import { ILogger } from '../../utils/custom.logger' - -@controller('/v1/fitbit/subscriber') -export class FitbitSubscriberController { - constructor( - @inject(Identifier.USER_AUTH_DATA_SERVICE) private readonly _userAuthDataService: IUserAuthDataService, - @inject(Identifier.LOGGER) private readonly _logger: ILogger - ) { - } - - @httpGet('/') - public async verifySubscriberDefault(@request() req: Request, @response() res: Response): Promise { - return req.query.filters.verify && req.query.filters.verify === `${process.env.FITBIT_CLIENT_SUBSCRIBER}` ? - res.status(HttpStatus.NO_CONTENT).send() : res.status(HttpStatus.NOT_FOUND).send() - } - - @httpPost('/') - public async getInfo(@request() req: Request, @response() res: Response): Promise { - this._logger.info(`Prepare to sync ${req.body[0].collectionType} from ${req.body[0].ownerId}.`) - this._userAuthDataService.syncLastFitbitUserData(req.body[0].ownerId, req.body[0].collectionType, req.body[0].date).then() - return res.status(200).send() - } -} diff --git a/src/ui/controllers/user.fitbit.sync.controller.ts b/src/ui/controllers/user.fitbit.sync.controller.ts index bae1d1c..33391f0 100644 --- a/src/ui/controllers/user.fitbit.sync.controller.ts +++ b/src/ui/controllers/user.fitbit.sync.controller.ts @@ -33,7 +33,26 @@ export class UserFitbitSyncController { return res.status(HttpStatus.ACCEPTED).send(result.toJSON()) } catch (err) { const handlerError = ApiExceptionManager.build(err) + if (err.code) { + if ([1011, 1012, 1021].includes(err.code)) return res.status(HttpStatus.BAD_REQUEST).send(err) + else if (err.code === 1401) return res.status(HttpStatus.UNAUTHORIZED).send(err) + else if (err.code === 1429) return res.status(HttpStatus.TOO_MANY_REQUESTS).send(err) + return res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err) + } return res.status(handlerError.code).send(handlerError.toJSON()) } } + } + +// 1011 -> 1021 = 400 +// 1401 = 401 +// 1429 = 429 +// 1500 = 500 + +// * 1011 - Expired Token +// * 1012 - Invalid Token +// * 1021 - Invalid Refresh Token +// * 1401 - Invalid Client Credentials +// * 1429 - Too Many Requests +// * 1500 - Generic Error diff --git a/test/integration/routes/fitbit.subscriber.controller.spec.ts b/test/integration/routes/fitbit.subscriber.controller.spec.ts deleted file mode 100644 index bbf6ac9..0000000 --- a/test/integration/routes/fitbit.subscriber.controller.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Identifier } from '../../../src/di/identifiers' -import { App } from '../../../src/app' -import { expect } from 'chai' -import { DIContainer } from '../../../src/di/di' - -const app: App = DIContainer.get(Identifier.APP) -const request = require('supertest')(app.getExpress()) - -describe('Routes: FitbitSubscriber', () => { - describe('GET /v1/fitbit/subscriber', () => { - context('when validate fitbit client subscriber', () => { - it('should return status code 204 and no content', () => { - return request - .get(`/v1/fitbit/subscriber?verify=${process.env.FITBIT_CLIENT_SUBSCRIBER}`) - .set('Content-Type', 'application/json') - .expect(204) - .then(res => { - expect(res.body).to.eql({}) - }) - }) - }) - context('when the verification fails', () => { - it('should return status code 404 and no content', () => { - return request - .get('/v1/fitbit/subscriber?verify=invalid') - .set('Content-Type', 'application/json') - .expect(404) - .then(res => { - expect(res.body).to.eql({}) - }) - }) - }) - }) - describe('POST /v1/fitbit/subscriber', () => { - context('when the client receive a subscribe notification', () => { - it('should return status code 200 and no content', () => { - return request - .post('/v1/fitbit/subscriber') - .send([{ - ownerId: '1A2B3', - collectionType: 'body', - date: '2019-09-18' - }]) - .set('Content-Type', 'application/json') - .expect(200) - .then(res => { - expect(res.body).to.eql({}) - }) - }) - }) - }) -}) diff --git a/test/mocks/models/default.entity.mock.ts b/test/mocks/models/default.entity.mock.ts index 524de35..703162c 100644 --- a/test/mocks/models/default.entity.mock.ts +++ b/test/mocks/models/default.entity.mock.ts @@ -134,6 +134,7 @@ export abstract class DefaultEntityMock { public static RESOURCE: any = { id: '5d7a9fc8d3f5bbb30e0d6a1e', + type: 'body', resource: { logId: '171847684' }, date_sync: '2019-09-12T13:36:49.741Z', user_id: '5d7a4a95c292db05e4f765a8', diff --git a/test/unit/repositories/fitbit.data.repository.spec.ts b/test/unit/repositories/fitbit.data.repository.spec.ts index 3fbd2db..32c246f 100644 --- a/test/unit/repositories/fitbit.data.repository.spec.ts +++ b/test/unit/repositories/fitbit.data.repository.spec.ts @@ -105,44 +105,6 @@ describe('Repositories: FitbitDataRepository', () => { }) }) - describe('subscribeUserEvent()', () => { - context('when subscribe in a specific event', () => { - it('should return undefined', () => { - return repo.subscribeUserEvent(data.fitbit!, 'activities', 'ACTIVITIES') - .then(res => { - assert.isUndefined(res) - }) - }) - }) - context('when a error occurs', () => { - it('should reject an error', () => { - return repo.subscribeUserEvent(data.fitbit!, 'error', 'ACTIVITIES') - .catch(err => { - assert.propertyVal(err, 'message', 'An error occurs!') - }) - }) - }) - }) - - describe('unsubscribeUserEvent()', () => { - context('when unsubscribe in a specific event', () => { - it('should return undefined', () => { - return repo.unsubscribeUserEvent(data.fitbit!, 'activities', 'ACTIVITIES') - .then(res => { - assert.isUndefined(res) - }) - }) - }) - context('when a error occurs', () => { - it('should reject an error', () => { - return repo.unsubscribeUserEvent(data.fitbit!, 'error', 'ACTIVITIES') - .catch(err => { - assert.propertyVal(err, 'message', 'An error occurs!') - }) - }) - }) - }) - describe('updateLastSync()', () => { context('when update the last data sync', () => { it('should return true', () => { diff --git a/test/unit/services/user.auth.data.service.spec.ts b/test/unit/services/user.auth.data.service.spec.ts index d6ca250..d76e47e 100644 --- a/test/unit/services/user.auth.data.service.spec.ts +++ b/test/unit/services/user.auth.data.service.spec.ts @@ -7,7 +7,6 @@ import { IUserAuthDataService } from '../../../src/application/port/user.auth.da import { assert } from 'chai' import { FitbitAuthData } from '../../../src/application/domain/model/fitbit.auth.data' import { Query } from '../../../src/infrastructure/repository/query/query' -import moment = require('moment') import { CustomLoggerMock } from '../../mocks/custom.logger.mock' import { EventBusRabbitMQMock } from '../../mocks/eventbus/eventbus.rabbitmq.mock' import { DataSync } from '../../../src/application/domain/model/data.sync' @@ -175,7 +174,6 @@ describe('Services: UserAuthDataService', () => { it('should reject an error', () => { return service.syncFitbitDataFromUser(DefaultEntityMock.USER_IDS.expired_token) .catch(err => { - assert.propertyVal(err, 'type', 'expired_token') assert.propertyVal(err, 'message', 'The token has expired') }) }) @@ -184,7 +182,6 @@ describe('Services: UserAuthDataService', () => { it('should reject an error', () => { return service.syncFitbitDataFromUser(DefaultEntityMock.USER_IDS.invalid_token) .catch(err => { - assert.propertyVal(err, 'type', 'invalid_token') assert.property(err, 'message') assert.propertyVal(err, 'description', 'Please make a new Fitbit Auth data and try again.') }) @@ -194,7 +191,6 @@ describe('Services: UserAuthDataService', () => { it('should reject an error', () => { return service.syncFitbitDataFromUser(DefaultEntityMock.USER_IDS.client_error) .catch(err => { - assert.propertyVal(err, 'type', 'client_error') assert.propertyVal(err, 'message', 'The Fitbit Client is unavailable') }) }) @@ -208,15 +204,4 @@ describe('Services: UserAuthDataService', () => { }) }) }) - describe('syncLastFitbitUserData()', () => { - context('when user does not exists', () => { - it('should return undefined', () => { - return service - .syncLastFitbitUserData('XXYYXX', 'weight', moment().format('YYYY-MM-DD')) - .then(res => { - assert.isUndefined(res) - }) - }) - }) - }) })