From 235693a9e2617fab1f660f21d351255425a7174d Mon Sep 17 00:00:00 2001 From: Sebastian Lange Date: Mon, 29 Apr 2019 14:04:25 +0200 Subject: [PATCH] refactor: initialise settings from config module and persist only the values Closes #30, #59 --- e2e/src/app.po.ts | 3 +- package-lock.json | 1218 ++++++++++++++++- package.json | 43 +- src/app/_helpers/fake-backend.interceptor.ts | 99 +- src/app/app.component.ts | 2 +- .../settings/item/settings-item.component.ts | 46 +- .../modules/settings/item/settings-item.html | 17 +- .../settings/page/settings-page.component.ts | 68 +- .../modules/settings/page/settings-page.html | 15 +- src/app/modules/settings/settings.module.ts | 4 +- .../settings/settings.provider.spec.ts | 191 ++- src/app/modules/settings/settings.provider.ts | 207 ++- src/assets/i18n/de.json | 8 +- src/assets/i18n/en.json | 8 +- 14 files changed, 1673 insertions(+), 256 deletions(-) diff --git a/e2e/src/app.po.ts b/e2e/src/app.po.ts index a9c4da83..181acbae 100644 --- a/e2e/src/app.po.ts +++ b/e2e/src/app.po.ts @@ -18,8 +18,7 @@ export class StAppsApp { getPageTitle() { return element(by.tagName('ion-router-outlet')) - .element(by.tagName('ion-title')) - .element(by.id('title')).getText(); + .element(by.tagName('ion-title')).getText(); } getTitle() { diff --git a/package-lock.json b/package-lock.json index 91fda47a..2e48a8bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -236,26 +236,26 @@ } }, "@angular/common": { - "version": "7.2.12", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.12.tgz", - "integrity": "sha512-JzcysRDfx2dvvcZ4uwgn+6gFDYlbH9j2Uyz6fWOSinAA0kcleOu/Gb77XbCI5M3Xvh1hxHVyz0Zxv/Pi0Y1O1g==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.14.tgz", + "integrity": "sha512-c2QBhVpbQhg1FDhOQkyVdFvU11mfvYHW5ZaXzxdCpq2rZXCureYiCSnlv++EsIAKqi22+2a6GACHF9Gh8kBmSg==", "requires": { "tslib": "^1.9.0" } }, "@angular/compiler": { - "version": "7.2.12", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.12.tgz", - "integrity": "sha512-B1N+/ECqIQz7PD2Zjb/21OOCmrXkl8DSXCBGNMXOSng+uYJM4dFPWkYkaAeHj8gcLDHvIPWlsapJ6JqyB3RPxA==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.14.tgz", + "integrity": "sha512-Idhs+5HIzx+1+hrXIDaRpSqobMB7UvSvPlvCvtb3EDYjmltTNG68TtwMzGM8W2jdayliYuFOjFrnw1wCTkK3Ag==", "dev": true, "requires": { "tslib": "^1.9.0" } }, "@angular/compiler-cli": { - "version": "7.2.12", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.12.tgz", - "integrity": "sha512-EXJuN9XuYjO9gwe5JWfatb64ljPjItZh5bd1MQtntMJONS4ntOtwiCd9RuNVK2ZXM7Co9PcvzE1qynB6tMseCw==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.14.tgz", + "integrity": "sha512-w5qn1nIPjiCP3WdbqicofpKpiRlh6NMYjWhe6mJysSBnVd34aSuGisYW/gVPQrmD46E1gmfpWTnWPeABVnjj6w==", "dev": true, "requires": { "canonical-path": "1.0.0", @@ -471,55 +471,55 @@ } }, "@angular/core": { - "version": "7.2.12", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.12.tgz", - "integrity": "sha512-E5BtJPL4Fz1xyGTsoE4LELoZ33x0S0lJ0iUKqLRFeKKYM/fJ8l8mVNSZ7LnURTwpcjCiHcCRMcaCfuXWj7Kqhw==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.14.tgz", + "integrity": "sha512-XeZZJCyBKSKo0E/7Ef0SfJejmn+E7uBXa5cR1QapafS0Hnrq/hZu/NI039IDU/51NoycMDH2vTV19SmKu9Mkow==", "requires": { "tslib": "^1.9.0" } }, "@angular/forms": { - "version": "7.2.12", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.12.tgz", - "integrity": "sha512-gQU8663C9LOyuza87XDkEQ2HXbaLt3LY8X45swUNfe+BvpKNhF5ZwwY22d93wuzvm/vFrCTPK7949ImfUW0XnA==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.14.tgz", + "integrity": "sha512-jL5YbTk7VZmz4l0++iFVUNa1vGM+nnALjHKi1Ub8VWioRDRboYUsHyxzlgWW9gZRbHpnLEXFiUz1td+v7TouJw==", "requires": { "tslib": "^1.9.0" } }, "@angular/http": { - "version": "7.2.12", - "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.2.12.tgz", - "integrity": "sha512-cceU+SWIQFOL3gRKqm5SYHQM3VoKbXne2XQRSY2Fdoc3XsipOzTEvJQPLm5ZcTEXdmRFlfianDoDaWEIvGbXkw==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.2.14.tgz", + "integrity": "sha512-rSdH2JojApDU83qVm7RabIlNo3Ni3yr2gwsmQWs4XZ7SC8jLNnDkzdUbQ6T0vfuVX3v/FtAuMJl0yaVcG3EUJg==", "requires": { "tslib": "^1.9.0" } }, "@angular/language-service": { - "version": "7.2.12", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.12.tgz", - "integrity": "sha512-dHHcAtCQ+ECoZa/bkm1diMZuxy/e+x2/qzClfKquO47EPqOIXYKCKZRqgGNHxdbUSRpmIEanfj/li4S7doCHZw==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.14.tgz", + "integrity": "sha512-YTU4ePAKikbIxNae9Qta8qaDArPgek7nhLEW9QfvrUAnpF7BkVboEI+7yLX5+NTfGf9cQ9cUfQ0TEreV+tMs7A==", "dev": true }, "@angular/platform-browser": { - "version": "7.2.12", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.12.tgz", - "integrity": "sha512-rhKxUtWM6LfM0cK0kVzQpdnzfGeL3KImk6kNn+RrZiXLk2N/pnwbrzfd6VUtm+zdg54S4BO8ui1NahwIC/PSKw==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.14.tgz", + "integrity": "sha512-yAq2+3W4J4B48HEmZYQucdEb9AHwRnv72q9CC/SxU7g59vaLhl1nv7cAWGJ4XFaJTbB7aB4Y4rLffuR+Gxkn7A==", "requires": { "tslib": "^1.9.0" } }, "@angular/platform-browser-dynamic": { - "version": "7.2.12", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.12.tgz", - "integrity": "sha512-maKmjCTaS+jrXnor9qVJZfkWAKrt6neIlYrjvcr9v2YUqv9vdMcd5WRaODvIXBqwh65gpMxk3hbZ48Yjh8EbBQ==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.14.tgz", + "integrity": "sha512-lmTCBiDRbOPtniIqBjm1n5jl1TdyQM0qWQdBcoCsKpMNS/6/RacRcQsJZApAMdWm6gIVuLgmRQzaCLkSoekfYA==", "requires": { "tslib": "^1.9.0" } }, "@angular/router": { - "version": "7.2.12", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.12.tgz", - "integrity": "sha512-n7EFKuOa6YDDvGZT/t7mXfQMuomkTVPJcWkpfPrViAKi4mcUnaU5IqYiBnv/WJfDDqocVD/Yf9YQD9zAajthEw==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.14.tgz", + "integrity": "sha512-uqg0SKy79voEOIOvzVbCzFDD9XOAfZWkYt01ca2qLFXMx+6jWeVQIDuXc8Dmz5udIXNK5Ae//9R+nt5UZUZrSA==", "requires": { "tslib": "^1.9.0" } @@ -1309,9 +1309,9 @@ } }, "@openstapps/core": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@openstapps/core/-/core-0.15.0.tgz", - "integrity": "sha512-jIX13rU2c2XPJD3qqnSZMLuP//wehJQ1L2yGBoeyMglLgg8WpfBHOsUqDqMky8qw1coXN55n9NG7am5Fz4gdxw==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@openstapps/core/-/core-0.17.0.tgz", + "integrity": "sha512-EWJHWyYrdDmKHt3JcZRDK5J9LkDkwfH64Uz4CNuCiGXDMHZAWc3YHTOX+shAqE+JmORGmvkwJ4t4vFrqhwHWJw==", "requires": { "@types/geojson": "1.0.6", "@types/json-patch": "0.0.30", @@ -1464,9 +1464,9 @@ "integrity": "sha512-6kBKf64aVfx93UJrcyEZ+OBM5nGv4RLsI6sR1Ar34bpgvGVRoyTgpxn4ZmtxOM5aDTAaaznYuYUH8bUX3Nk3YA==" }, "@types/node": { - "version": "11.13.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.2.tgz", - "integrity": "sha512-HOtU5KqROKT7qX/itKHuTtt5fV0iXbheQvrgbLNXFJQBY/eh+VS5vmmTAVlo3qIGMsypm0G4N1t2AXjy1ZicaQ==", + "version": "10.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz", + "integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg==", "dev": true }, "@types/nodemailer": { @@ -1791,6 +1791,12 @@ "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", "dev": true }, + "add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha1-anmQQ3ynNtXhKI25K9MmbV9csqo=", + "dev": true + }, "adm-zip": { "version": "0.4.13", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", @@ -2018,14 +2024,19 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true, - "optional": true + "dev": true }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", + "dev": true + }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -3008,15 +3019,13 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true, - "optional": true + "dev": true }, "camelcase-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, - "optional": true, "requires": { "camelcase": "^2.0.0", "map-obj": "^1.0.0" @@ -3391,6 +3400,16 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "compare-func": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.2.tgz", + "integrity": "sha1-md0LpFfh+bxyKxLAjsM+6rMfpkg=", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^3.0.0" + } + }, "compare-versions": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz", @@ -3527,6 +3546,630 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, + "conventional-changelog": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.4.tgz", + "integrity": "sha512-uMeTSzEb2oKFlL00Oh9j3+00PFq1MNneLzyy0TBftxo4PFrs7OiaRJXmXtEgSvJDdkc0RSd6ch2N+yTxPagZ0A==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.3", + "conventional-changelog-atom": "^2.0.1", + "conventional-changelog-codemirror": "^2.0.1", + "conventional-changelog-conventionalcommits": "^1.1.2", + "conventional-changelog-core": "^3.2.2", + "conventional-changelog-ember": "^2.0.2", + "conventional-changelog-eslint": "^3.0.2", + "conventional-changelog-express": "^2.0.1", + "conventional-changelog-jquery": "^3.0.4", + "conventional-changelog-jshint": "^2.0.1", + "conventional-changelog-preset-loader": "^2.1.1" + } + }, + "conventional-changelog-angular": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz", + "integrity": "sha512-YD1xzH7r9yXQte/HF9JBuEDfvjxxwDGGwZU1+ndanbY0oFgA+Po1T9JDSpPLdP0pZT6MhCAsdvFKC4TJ4MTJTA==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "q": "^1.5.1" + } + }, + "conventional-changelog-atom": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.1.tgz", + "integrity": "sha512-9BniJa4gLwL20Sm7HWSNXd0gd9c5qo49gCi8nylLFpqAHhkFTj7NQfROq3f1VpffRtzfTQp4VKU5nxbe2v+eZQ==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-cli": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-2.0.12.tgz", + "integrity": "sha512-6wh9W5Gpr9DM40E8cFi0qa6JotVm4Jq+suksuqgKnm544H8ZXsRhgGNXShDASOteY9brv9fX8/+fE/QL1wHqbA==", + "dev": true, + "requires": { + "add-stream": "^1.0.0", + "conventional-changelog": "^3.0.6", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "tempfile": "^1.1.1" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + } + } + }, + "conventional-changelog-codemirror": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.1.tgz", + "integrity": "sha512-23kT5IZWa+oNoUaDUzVXMYn60MCdOygTA2I+UjnOMiYVhZgmVwNd6ri/yDlmQGXHqbKhNR5NoXdBzSOSGxsgIQ==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-conventionalcommits": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-1.1.2.tgz", + "integrity": "sha512-t8VyibJHGrtsDwSHjgpW9v7oBbqDGQooCMo/a2rc0z5cousV5O11palcSPpyshEVWVijxPtzBNG02EQkMDJ8CA==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "q": "^1.5.1" + } + }, + "conventional-changelog-core": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.2.2.tgz", + "integrity": "sha512-cssjAKajxaOX5LNAJLB+UOcoWjAIBvXtDMedv/58G+YEmAXMNfC16mmPl0JDOuVJVfIqM0nqQiZ8UCm8IXbE0g==", + "dev": true, + "requires": { + "conventional-changelog-writer": "^4.0.5", + "conventional-commits-parser": "^3.0.2", + "dateformat": "^3.0.0", + "get-pkg-repo": "^1.0.0", + "git-raw-commits": "2.0.0", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^2.0.2", + "lodash": "^4.2.1", + "normalize-package-data": "^2.3.5", + "q": "^1.5.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", + "through2": "^3.0.0" + }, + "dependencies": { + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "conventional-changelog-ember": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.2.tgz", + "integrity": "sha512-qtZbA3XefO/n6DDmkYywDYi6wDKNNc98MMl2F9PKSaheJ25Trpi3336W8fDlBhq0X+EJRuseceAdKLEMmuX2tg==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-eslint": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.2.tgz", + "integrity": "sha512-Yi7tOnxjZLXlCYBHArbIAm8vZ68QUSygFS7PgumPRiEk+9NPUeucy5Wg9AAyKoBprSV3o6P7Oghh4IZSLtKCvQ==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-express": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.1.tgz", + "integrity": "sha512-G6uCuCaQhLxdb4eEfAIHpcfcJ2+ao3hJkbLrw/jSK/eROeNfnxCJasaWdDAfFkxsbpzvQT4W01iSynU3OoPLIw==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-jquery": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.4.tgz", + "integrity": "sha512-IVJGI3MseYoY6eybknnTf9WzeQIKZv7aNTm2KQsiFVJH21bfP2q7XVjfoMibdCg95GmgeFlaygMdeoDDa+ZbEQ==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-jshint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.1.tgz", + "integrity": "sha512-kRFJsCOZzPFm2tzRHULWP4tauGMvccOlXYf3zGeuSW4U0mZhk5NsjnRZ7xFWrTFPlCLV+PNmHMuXp5atdoZmEg==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "q": "^1.5.1" + } + }, + "conventional-changelog-preset-loader": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.1.1.tgz", + "integrity": "sha512-K4avzGMLm5Xw0Ek/6eE3vdOXkqnpf9ydb68XYmCc16cJ99XMMbc2oaNMuPwAsxVK6CC1yA4/I90EhmWNj0Q6HA==", + "dev": true + }, + "conventional-changelog-writer": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.5.tgz", + "integrity": "sha512-g/Myp4MaJ1A+f7Ai+SnVhkcWtaHk6flw0SYN7A+vQ+MTu0+gSovQWs4Pg4NtcNUcIztYQ9YHsoxHP+GGQplI7Q==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "conventional-commits-filter": "^2.0.2", + "dateformat": "^3.0.0", + "handlebars": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "semver": "^5.5.0", + "split": "^1.0.0", + "through2": "^3.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2" + } + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + } + } + }, + "conventional-commits-filter": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz", + "integrity": "sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ==", + "dev": true, + "requires": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + } + }, + "conventional-commits-parser": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.2.tgz", + "integrity": "sha512-y5eqgaKR0F6xsBNVSQ/5cI5qIF3MojddSUi1vKIggRkqUTbkqFKH9P5YX/AT1BVZp9DtSzBTIkvjyVLotLsVog==", + "dev": true, + "requires": { + "JSONStream": "^1.0.4", + "is-text-path": "^1.0.0", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "split2": "^2.0.0", + "through2": "^3.0.0", + "trim-off-newlines": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + } + } + }, "convert-source-map": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", @@ -3858,7 +4501,6 @@ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, - "optional": true, "requires": { "array-find-index": "^1.0.1" } @@ -3887,6 +4529,15 @@ "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=", "dev": true }, + "dargs": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz", + "integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -3907,6 +4558,12 @@ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", "dev": true }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -3921,6 +4578,16 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + } + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -3943,6 +4610,11 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deepmerge": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.2.0.tgz", + "integrity": "sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow==" + }, "default-gateway": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz", @@ -4252,6 +4924,15 @@ "integrity": "sha1-xzdwGfxOVQeYkosrmv62ar+h8vk=", "dev": true }, + "dot-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", + "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, "duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", @@ -5890,12 +6571,24 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, + "get-pkg-repo": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", + "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "meow": "^3.3.0", + "normalize-package-data": "^2.3.0", + "parse-github-repo-url": "^1.3.0", + "through2": "^2.0.0" + } + }, "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true, - "optional": true + "dev": true }, "get-stream": { "version": "3.0.0", @@ -5917,6 +6610,294 @@ "assert-plus": "^1.0.0" } }, + "git-raw-commits": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.0.tgz", + "integrity": "sha512-w4jFEJFgKXMQJ0H0ikBk2S+4KP2VEjhCvLCNqbNRQC8BgGWgLKNCO7a9K9LI+TVT7Gfoloje502sEnctibffgg==", + "dev": true, + "requires": { + "dargs": "^4.0.1", + "lodash.template": "^4.0.2", + "meow": "^4.0.0", + "split2": "^2.0.0", + "through2": "^2.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + } + } + }, + "git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", + "dev": true, + "requires": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "git-semver-tags": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.2.tgz", + "integrity": "sha512-34lMF7Yo1xEmsK2EkbArdoU79umpvm0MfzaDkSNYSJqtM5QLAVTPWgpiXSVI5o/O9EvZPSrP4Zvnec/CqhSd5w==", + "dev": true, + "requires": { + "meow": "^4.0.0", + "semver": "^5.5.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + } + } + }, + "gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", + "dev": true, + "requires": { + "ini": "^1.3.2" + } + }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -6529,7 +7510,6 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, - "optional": true, "requires": { "repeating": "^2.0.0" } @@ -6862,6 +7842,12 @@ } } }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -6886,6 +7872,12 @@ "path-is-inside": "^1.0.1" } }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -6932,6 +7924,15 @@ "has-symbols": "^1.0.0" } }, + "is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", + "dev": true, + "requires": { + "text-extensions": "^1.0.0" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -6950,8 +7951,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true, - "optional": true + "dev": true }, "is-windows": { "version": "1.0.2", @@ -7726,7 +8726,6 @@ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -7739,15 +8738,13 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "optional": true + "dev": true }, "strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, - "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -7803,6 +8800,12 @@ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, "lodash.assign": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", @@ -7822,6 +8825,12 @@ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, + "lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", + "dev": true + }, "lodash.mergewith": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", @@ -7835,6 +8844,25 @@ "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", "dev": true }, + "lodash.template": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", + "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", + "dev": true, + "requires": { + "lodash._reinterpolate": "~3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", + "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", + "dev": true, + "requires": { + "lodash._reinterpolate": "~3.0.0" + } + }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", @@ -7894,7 +8922,6 @@ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true, - "optional": true, "requires": { "currently-unhandled": "^0.4.1", "signal-exit": "^3.0.0" @@ -8062,8 +9089,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "optional": true + "dev": true }, "map-stream": { "version": "0.1.0", @@ -8127,7 +9153,6 @@ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, - "optional": true, "requires": { "camelcase-keys": "^2.0.0", "decamelize": "^1.1.2", @@ -8145,8 +9170,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true, - "optional": true + "dev": true } } }, @@ -8274,6 +9298,16 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, "minipass": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", @@ -8366,6 +9400,12 @@ "minimist": "0.0.8" } }, + "modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true + }, "moment": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", @@ -9421,6 +10461,12 @@ "safe-buffer": "^5.1.1" } }, + "parse-github-repo-url": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", + "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=", + "dev": true + }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -10071,6 +11117,12 @@ "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", "dev": true }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, "quote-stream": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz", @@ -10166,7 +11218,6 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, - "optional": true, "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -10178,7 +11229,6 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -10189,8 +11239,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "optional": true + "dev": true } } }, @@ -10199,7 +11248,6 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, - "optional": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -10210,7 +11258,6 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, - "optional": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -10221,7 +11268,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, - "optional": true, "requires": { "pinkie-promise": "^2.0.0" } @@ -10276,7 +11322,6 @@ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, - "optional": true, "requires": { "indent-string": "^2.1.0", "strip-indent": "^1.0.1" @@ -11407,6 +12452,15 @@ "extend-shallow": "^3.0.0" } }, + "split2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", + "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", + "dev": true, + "requires": { + "through2": "^2.0.2" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -11686,7 +12740,6 @@ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, - "optional": true, "requires": { "get-stdin": "^4.0.1" } @@ -11914,6 +12967,24 @@ "inherits": "2" } }, + "tempfile": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-1.1.1.tgz", + "integrity": "sha1-W8xOrsxKsscH2LwR2ZzMmiyyh/I=", + "dev": true, + "requires": { + "os-tmpdir": "^1.0.0", + "uuid": "^2.0.1" + }, + "dependencies": { + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", + "dev": true + } + } + }, "terser": { "version": "3.17.0", "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", @@ -12110,6 +13181,12 @@ } } }, + "text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -12258,8 +13335,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true, - "optional": true + "dev": true + }, + "trim-off-newlines": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", + "dev": true }, "trim-right": { "version": "1.0.1", diff --git a/package.json b/package.json index 47b7d844..5125e59c 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "scripts": { "build": "ng build", "build:prod": "ng build --prod", + "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && git commit -m 'docs: update changelog'", + "check-configuration": "openstapps-configuration", "e2e": "ng e2e", "docker:build": "sudo docker run -p 8100:8100 -p 35729:35729 -p 53703:53703 -v $PWD:/app -it registry.gitlab.com/openstapps/app bash -c \"npm install && npm run build\"", "docker:enter": "sudo docker run -p 8100:8100 -p 35729:35729 -p 53703:53703 -v $PWD:/app -it registry.gitlab.com/openstapps/app bash", @@ -25,13 +27,13 @@ "test": "ng test" }, "dependencies": { - "@angular/common": "7.2.12", - "@angular/core": "7.2.12", - "@angular/forms": "7.2.12", - "@angular/http": "7.2.12", - "@angular/platform-browser": "7.2.12", - "@angular/platform-browser-dynamic": "7.2.12", - "@angular/router": "7.2.12", + "@angular/common": "7.2.14", + "@angular/core": "7.2.14", + "@angular/forms": "7.2.14", + "@angular/http": "7.2.14", + "@angular/platform-browser": "7.2.14", + "@angular/platform-browser-dynamic": "7.2.14", + "@angular/router": "7.2.14", "@ionic-native/core": "5.4.0", "@ionic-native/geolocation": "5.4.0", "@ionic-native/splash-screen": "5.4.0", @@ -42,7 +44,7 @@ "@ngx-translate/http-loader": "4.0.0", "@openstapps/api": "0.6.0", "@openstapps/configuration": "0.8.0", - "@openstapps/core": "0.15.0", + "@openstapps/core": "0.17.0", "@openstapps/logger": "0.0.5", "cordova-android": "8.0.0", "cordova-browser": "6.0.0", @@ -54,6 +56,7 @@ "cordova-plugin-splashscreen": "5.0.2", "cordova-plugin-whitelist": "1.3.3", "core-js": "2.6.5", + "deepmerge": "3.2.0", "moment": "2.24.0", "ngx-markdown": "7.1.4", "ngx-moment": "3.4.0", @@ -66,16 +69,17 @@ "@angular-devkit/core": "7.3.8", "@angular-devkit/schematics": "7.3.8", "@angular/cli": "7.3.8", - "@angular/compiler": "7.2.12", - "@angular/compiler-cli": "7.2.12", - "@angular/language-service": "7.2.12", + "@angular/compiler": "7.2.14", + "@angular/compiler-cli": "7.2.14", + "@angular/language-service": "7.2.14", "@compodoc/compodoc": "1.1.9", "@ionic/ng-toolkit": "1.1.0", "@ionic/schematics-angular": "1.0.7", "@types/jasmine": "3.3.12", "@types/jasminewd2": "2.0.6", - "@types/node": "11.13.2", + "@types/node": "10.14.6", "codelyzer": "5.0.0", + "conventional-changelog-cli": "2.0.12", "is-docker": "1.1.0", "jasmine-core": "3.4.0", "jasmine-spec-reporter": "4.2.1", @@ -107,5 +111,20 @@ "browser", "android" ] + }, + "openstappsConfiguration": { + "forPackaging": false, + "hasCli": false, + "ignoreCiEntries": [ + "image", + "pages" + ], + "ignoreScripts": [ + "prepublishOnly", + "compile" + ], + "serverSide": false, + "standardBuild": false, + "standardDocumentation": false } } diff --git a/src/app/_helpers/fake-backend.interceptor.ts b/src/app/_helpers/fake-backend.interceptor.ts index f1442a03..5feee4b7 100644 --- a/src/app/_helpers/fake-backend.interceptor.ts +++ b/src/app/_helpers/fake-backend.interceptor.ts @@ -15,7 +15,7 @@ import {HTTP_INTERCEPTORS, HttpClient, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http'; import {Injectable} from '@angular/core'; -import {SCIndexResponse, SCThingType} from '@openstapps/core'; +import {SCIndexResponse, SCThingOriginType, SCThingType} from '@openstapps/core'; import {Observable, of} from 'rxjs'; import {map, delay} from 'rxjs/operators'; import {SampleThings} from './data/sample-things'; @@ -55,7 +55,102 @@ const sampleIndexResponse: SCIndexResponse = { menus: [], name: 'StApps - Technische Universität Berlin', privacyPolicyUrl: 'https://stappsbe01.innocampus.tu-berlin.de/_static/privacy.md', - settings: [], + settings: [ + { + categories: ['profile'], + description: '', + input: { + defaultValue: 'student', + inputType: 'singleChoice', + values: ['student', 'employee', 'guest'], + }, + name: 'group', + order: 1, + origin: { + indexed: '2018-09-11T12:30:00Z', + name: 'Dummy', + type: SCThingOriginType.Remote, + }, + translations: { + de: { + categories: ['Benutzer'], + description: 'Mit welcher Benutzergruppe soll die App verwendet werden?' + + ' Die Einstellung wird beispielsweise für die Vorauswahl der Preiskategorie der Mensa verwendet.', + name: 'Gruppe', + }, + en: { + categories: ['User'], + description: 'The user group the app is going to be used.' + + 'This settings for example is getting used for the predefined price category of mensa meals.', + name: 'Group', + }, + }, + type: SCThingType.Setting, + uid: '', + }, + { + categories: ['profile'], + description: '', + input: { + defaultValue: 'en', + inputType: 'singleChoice', + values: ['en', 'de'], + }, + name: 'language', + order: 0, + origin: { + indexed: '2018-09-11T12:30:00Z', + name: 'Dummy', + type: SCThingOriginType.Remote, + }, + translations: { + de: { + categories: ['Benutzer'], + description: 'Die Sprache in der die App angezeigt werden soll', + name: 'Sprache', + }, + en: { + categories: ['User'], + description: 'The language this app is going to use', + name: 'Language', + }, + }, + type: SCThingType.Setting, + uid: '', + }, + { + categories: ['privacy'], + description: '', + input: { + defaultValue: false, + inputType: 'singleChoice', + values: [true, false], + }, + name: 'geoLocation', + order: 0, + origin: { + indexed: '2018-09-11T12:30:00Z', + name: 'Dummy', + type: SCThingOriginType.Remote, + }, + translations: { + de: { + categories: ['Privatsphäre'], + description: 'Berechtigung für die Verwendung des Ortungsdienstes, für die Anzeige der aktuellen ' + + 'Position \'\n auf der Karte und zur Berechnung der Entfernung zu Gebäuden und Orten des Campus', + name: 'Position', + }, + en: { + categories: ['Privacy'], + description: 'Allow the App to use the device location to provide additional informationsbased ' + + 'on your actual location', + name: 'Position', + }, + }, + type: SCThingType.Setting, + uid: '', + }, + ], }, backend: { SCVersion: '1.0.0', diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b9bc27af..bd9e5b5c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -72,7 +72,7 @@ export class AppComponent { try { // set language from settings - const languageCode = await this.settingsProvider.getSettingValue('profile', 'language'); + const languageCode = await this.settingsProvider.getValue('profile', 'language'); this.translateService.use(languageCode); } catch (error) { this.logger.warn(error); diff --git a/src/app/modules/settings/item/settings-item.component.ts b/src/app/modules/settings/item/settings-item.component.ts index 0fa89dbe..a3d5fad9 100644 --- a/src/app/modules/settings/item/settings-item.component.ts +++ b/src/app/modules/settings/item/settings-item.component.ts @@ -13,14 +13,14 @@ * this program. If not, see . */ import {Component, Input} from '@angular/core'; -import {Geolocation} from '@ionic-native/geolocation/ngx'; import {AlertController} from '@ionic/angular'; import {LangChangeEvent, TranslateService} from '@ngx-translate/core'; import { SCSetting, - SCSettingMeta, + SCThingTranslator, SCTranslations, } from '@openstapps/core'; +import {Logger} from '@openstapps/logger'; import {SettingsProvider} from '../settings.provider'; @Component({ @@ -31,22 +31,21 @@ export class SettingsItemComponent { isVisible = true; // limit to languages that are available in StApps Core language: keyof SCTranslations; - meta = SCSettingMeta; - + logger = new Logger(); @Input() setting: SCSetting; + translator: SCThingTranslator; constructor(private alertCtrl: AlertController, private translateService: TranslateService, - private settingsProvider: SettingsProvider, - private geoLocation: Geolocation) { - this.meta = SCSettingMeta; - + private settingsProvider: SettingsProvider) { this.language = translateService.currentLang as keyof SCTranslations; + this.translator = new SCThingTranslator(this.language, 'de'); translateService.onLangChange.subscribe((event: LangChangeEvent) => { this.isVisible = false; this.language = event.lang as keyof SCTranslations; - // workaround for selected 'select option' not updating translation + this.translator = new SCThingTranslator(this.language, 'de'); + // TODO: Issue #53 check workaround for selected 'select option' not updating translation setTimeout(() => this.isVisible = true); }); } @@ -56,24 +55,11 @@ export class SettingsItemComponent { * if no permission is granted, setting is set to false and an alert is presented to the user */ private async checkGeoLocationPermission() { - // request geoLocation to test the user permission - try { - // set enableHighAccuracy, otherwise android platform does not respond - const options = { - enableHighAccuracy: true, - }; - await this.geoLocation.getCurrentPosition(options); - } catch (error) { - // if error code is 1 the user denied permission, - // other errors like 'timeout' or 'no location' will be ignored here - if (error.code === 1) { - // ios has special error message for disabled location services, for the setting we ignore it - if (error.message.toLowerCase() !== 'location services are disabled.') { - // revert setting value - this.setting.input.value = false; - await this.presentGeoLocationAlert(); - } - } + const permissionGranted = await this.settingsProvider.checkGeoLocationPermission(); + if (!permissionGranted) { + // revert setting value + this.setting.input.value = false; + await this.presentGeoLocationAlert(); } } @@ -81,8 +67,8 @@ export class SettingsItemComponent { * Shows alert with error message on denied user permission or disabled location services */ private async presentGeoLocationAlert() { - const title = this.translateService.instant('settings.geoLocation.permission_denied_title'); - const message = this.translateService.instant('settings.geoLocation.permission_denied_message'); + const title = await this.translateService.get('settings.geoLocation.permission_denied_title').toPromise(); + const message = await this.translateService.get('settings.geoLocation.permission_denied_message').toPromise(); await this.presentAlert(title, message); } @@ -124,7 +110,7 @@ export class SettingsItemComponent { } else { // reset setting this.setting.input.value = - await this.settingsProvider.getSettingValue(this.setting.categories[0], this.setting.name); + await this.settingsProvider.getValue(this.setting.categories[0], this.setting.name); } } diff --git a/src/app/modules/settings/item/settings-item.html b/src/app/modules/settings/item/settings-item.html index 6732c151..66d57d06 100644 --- a/src/app/modules/settings/item/settings-item.html +++ b/src/app/modules/settings/item/settings-item.html @@ -1,15 +1,11 @@ - + + {{ translator.translate(setting).name() }} + - + {{ translator.translate(setting).description() }}
- - - - @@ -45,11 +41,6 @@ - - - no template for {{ setting.name }} -
-
diff --git a/src/app/modules/settings/page/settings-page.component.ts b/src/app/modules/settings/page/settings-page.component.ts index 5d7c8136..2bd4d120 100644 --- a/src/app/modules/settings/page/settings-page.component.ts +++ b/src/app/modules/settings/page/settings-page.component.ts @@ -13,8 +13,9 @@ * this program. If not, see . */ import {Component} from '@angular/core'; +import {AlertController, ToastController} from '@ionic/angular'; import {LangChangeEvent, TranslateService} from '@ngx-translate/core'; -import {SCSettingMeta, SCTranslations} from '@openstapps/core'; +import {SCSettingMeta, SCThingTranslator, SCTranslations} from '@openstapps/core'; import {SettingsCache, SettingsProvider} from '../settings.provider'; @Component({ @@ -28,22 +29,81 @@ export class SettingsPageComponent { meta = SCSettingMeta; objectKeys = Object.keys; settingsCache: SettingsCache; + translator: SCThingTranslator; - constructor(public settingsProvider: SettingsProvider, - translateService: TranslateService) { + constructor(private alertController: AlertController, + private settingsProvider: SettingsProvider, + private toastController: ToastController, + private translateService: TranslateService) { this.language = translateService.currentLang as keyof SCTranslations; + this.translator = new SCThingTranslator(this.language, 'de'); + translateService.onLangChange.subscribe((event: LangChangeEvent) => { this.language = event.lang as keyof SCTranslations; + this.translator = new SCThingTranslator(this.language, 'de'); }); this.settingsCache = {}; this.categoriesOrder = settingsProvider.getCategoriesOrder(); + } + /** + * Presents a Toast with message for settings successful reset + */ + private async presentSettingsResetToast() { + const toast = await this.toastController.create({ + cssClass: 'text-center', + duration: 2000, + message: this.translateService.instant('settings.resetToast.message'), + }); + toast.present(); + } + + /** + * Loads cache of settings from SettingProvider + */ async loadSettings(): Promise { - this.settingsCache = await this.settingsProvider.getSettingsCache(); + this.settingsCache = await this.settingsProvider.getCache(); } async ngOnInit() { await this.loadSettings(); } + + /** + * Presents an alert to the user to reset settings to default values + */ + async presentResetAlert() { + const cancelText = await this.translateService.get('settings.resetAlert.buttonCancel').toPromise(); + const yesText = await this.translateService.get('settings.resetAlert.buttonYes').toPromise(); + const title = await this.translateService.get('settings.resetAlert.title').toPromise(); + const message = await this.translateService.get('settings.resetAlert.message').toPromise(); + + const alert = await this.alertController.create({ + buttons: [ + { + role: 'cancel', + text: cancelText, + }, + { + handler: async () => { + await this.resetSettings(); + }, + text: yesText, + }, + ], + header: title, + message: message, + }); + alert.present(); + } + + /** + * Resets all settings to default values + */ + async resetSettings() { + await this.settingsProvider.resetDefault(); + await this.loadSettings(); + await this.presentSettingsResetToast(); + } } diff --git a/src/app/modules/settings/page/settings-page.html b/src/app/modules/settings/page/settings-page.html index 1c7921b9..86ab5d74 100644 --- a/src/app/modules/settings/page/settings-page.html +++ b/src/app/modules/settings/page/settings-page.html @@ -1,20 +1,25 @@ - -
{{'settings.title' | translate}}
+ {{'settings.title' | translate}}
- +
-
{{ meta.getFieldValueTranslation(language, 'categories', - settingsCache[categoryKey].settings[objectKeys(settingsCache[categoryKey].settings)[0]]) }}
+ +
{{translator.translate(settingsCache[categoryKey].settings[objectKeys(settingsCache[categoryKey].settings)[0]]).categories()[0]}} +
+
+ + {{'settings.resetSettings' | translate}} + +
diff --git a/src/app/modules/settings/settings.module.ts b/src/app/modules/settings/settings.module.ts index 8b473848..05d08a1b 100644 --- a/src/app/modules/settings/settings.module.ts +++ b/src/app/modules/settings/settings.module.ts @@ -20,12 +20,13 @@ import {Geolocation} from '@ionic-native/geolocation/ngx'; import {IonicModule} from '@ionic/angular'; import {TranslateModule} from '@ngx-translate/core'; +import {ConfigProvider} from '../config/config.provider'; import {SettingsItemComponent} from './item/settings-item.component'; import {SettingsPageComponent} from './page/settings-page.component'; import {SettingsProvider} from './settings.provider'; const settingsRoutes: Routes = [ - { path: 'settings', component: SettingsPageComponent }, + {path: 'settings', component: SettingsPageComponent}, ]; @NgModule({ @@ -41,6 +42,7 @@ const settingsRoutes: Routes = [ RouterModule.forChild(settingsRoutes), ], providers: [ + ConfigProvider, Geolocation, SettingsProvider, ], diff --git a/src/app/modules/settings/settings.provider.spec.ts b/src/app/modules/settings/settings.provider.spec.ts index 1a3f6636..24062704 100644 --- a/src/app/modules/settings/settings.provider.spec.ts +++ b/src/app/modules/settings/settings.provider.spec.ts @@ -14,123 +14,165 @@ */ import {TestBed} from '@angular/core/testing'; import {SCSetting, SCThingOriginType, SCThingType} from '@openstapps/core'; -import {StorageModule} from '../storage/storage.module'; +import {ConfigProvider} from '../config/config.provider'; import {StorageProvider} from '../storage/storage.provider'; -import {SettingsProvider} from './settings.provider'; +import {SettingsProvider, SettingValuesContainer, STORAGE_KEY_SETTING_VALUES} from './settings.provider'; +import {Geolocation} from '@ionic-native/geolocation/ngx'; describe('SettingsProvider', () => { + let configProviderSpy: jasmine.SpyObj; let settingsProvider: SettingsProvider; - let storageModule: StorageProvider; + let storageProviderSpy: jasmine.SpyObj; beforeEach(async () => { - TestBed.configureTestingModule({ - imports: [StorageModule], - providers: [SettingsProvider, StorageProvider], - }); - settingsProvider = TestBed.get(SettingsProvider); - storageModule = TestBed.get(StorageProvider); + const storageProviderMethodSpy = jasmine.createSpyObj('StorageProvider', ['init', 'get', 'has', 'put']); + const configProviderMethodSpy = jasmine.createSpyObj('ConfigProvider', ['getValue']); - settingsProvider.clear(); + TestBed.configureTestingModule({ + imports: [], + providers: [ + SettingsProvider, + { + provide: StorageProvider, useValue: storageProviderMethodSpy, + }, + { + provide: ConfigProvider, useValue: configProviderMethodSpy, + }, + Geolocation, + ], + }); + configProviderSpy = TestBed.get(ConfigProvider); + // set settings returned from config + configProviderSpy.getValue.and.returnValue(Promise.resolve(CONFIG_SETTINGS_MOCK)); + settingsProvider = TestBed.get(SettingsProvider); + storageProviderSpy = TestBed.get(StorageProvider); + storageProviderMethodSpy.has.and.returnValue(false); }); it('should provide and get setting', async () => { - await settingsProvider.provideSetting(JSON.parse(JSON.stringify(SETTING_MOCKS[0]))); - const setting = await settingsProvider.getSetting(SETTING_MOCKS[0].categories[0], SETTING_MOCKS[0].name); - await expect(setting).toBeDefined(); + await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); + const setting: SCSetting = await settingsProvider + .getSetting(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name); + await expect(setting.input.value).toBeDefined(); }); it('should provide and get settings value', async () => { - await settingsProvider.provideSetting(JSON.parse(JSON.stringify(SETTING_MOCKS[0]))); - const value = await settingsProvider.getSettingValue(SETTING_MOCKS[0].categories[0], SETTING_MOCKS[0].name); - await expect(value).toBeDefined(); + await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); + const value = await settingsProvider + .getValue(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name); + await expect(value).toEqual(CONFIG_SETTINGS_MOCK[0].input.defaultValue); + }); + + it('should get persisted setting value', async () => { + // set return values of storage + storageProviderSpy.has.and.returnValue(Promise.resolve(true)); + storageProviderSpy.get.and.returnValue(Promise.resolve(SETTING_VALUES_MOCK)); + + const value = await settingsProvider + .getValue(CONFIG_SETTINGS_MOCK[3].categories[0], CONFIG_SETTINGS_MOCK[3].name); + await expect(value).toEqual(SETTING_VALUES_MOCK.profile.group); + }); + + it('should set default setting value if no persisted value exist', async () => { + // set return values of spy objects + storageProviderSpy.has.and.returnValue(Promise.resolve(true)); + storageProviderSpy.get.and.returnValue(Promise.resolve([])); + const value = await settingsProvider + .getValue(CONFIG_SETTINGS_MOCK[3].categories[0], CONFIG_SETTINGS_MOCK[3].name); + await expect(value).toEqual(CONFIG_SETTINGS_MOCK[3].input.defaultValue); + }); + + it('should keep persisted setting values from settings that are not contained in loaded config', async () => { + const settings = [ + CONFIG_SETTINGS_MOCK[4], + CONFIG_SETTINGS_MOCK[5], + ]; + configProviderSpy.getValue.and.returnValue(Promise.resolve(settings)); + storageProviderSpy.has.and.returnValue(Promise.resolve(true)); + storageProviderSpy.get.and.returnValue(Promise.resolve(SETTING_VALUES_MOCK)); + await settingsProvider.init(); + await expect(storageProviderSpy.put).toHaveBeenCalledWith(STORAGE_KEY_SETTING_VALUES, SETTING_VALUES_MOCK); }); it('should set value of a provided setting', async () => { - await settingsProvider.provideSetting(JSON.parse(JSON.stringify(SETTING_MOCKS[0]))); - await settingsProvider.setSettingValue(SETTING_MOCKS[0].categories[0], SETTING_MOCKS[0].name, 'updated'); - const value = await settingsProvider.getSettingValue(SETTING_MOCKS[0].categories[0], SETTING_MOCKS[0].name); + await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[1]))); + await settingsProvider + .setSettingValue(CONFIG_SETTINGS_MOCK[1].categories[0], CONFIG_SETTINGS_MOCK[1].name, 'updated'); + const value = await settingsProvider + .getValue(CONFIG_SETTINGS_MOCK[1].categories[0], CONFIG_SETTINGS_MOCK[1].name); await expect(value).toEqual('updated'); }); it('should return copy of settingsCache', async () => { - const category = SETTING_MOCKS[0].categories[0]; - const name = SETTING_MOCKS[0].name; - await settingsProvider.provideSetting(JSON.parse(JSON.stringify(SETTING_MOCKS[0]))); - const settings = await settingsProvider.getSettingsCache(); + const category = CONFIG_SETTINGS_MOCK[0].categories[0]; + const name = CONFIG_SETTINGS_MOCK[0].name; + await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); + const settings = await settingsProvider.getCache(); settings[category].settings[name].input.value = 'testValue'; // cached setting value should still be defaultValue - await expect((await settingsProvider.getSettingValue(category, name))) - .toEqual(SETTING_MOCKS[0].input.defaultValue); - }); - - it('should call storage put on provideSetting', async () => { - spyOn(storageModule, 'put'); - await settingsProvider.provideSetting(JSON.parse(JSON.stringify(SETTING_MOCKS[0]))); - await expect(storageModule.put).toHaveBeenCalled(); + await expect((await settingsProvider.getValue(category, name))) + .toEqual(CONFIG_SETTINGS_MOCK[0].input.defaultValue); }); it('should call storage put on setSettingValue', async () => { - await settingsProvider.provideSetting(JSON.parse(JSON.stringify(SETTING_MOCKS[0]))); - spyOn(storageModule, 'put'); - await settingsProvider.setSettingValue(SETTING_MOCKS[0].categories[0], SETTING_MOCKS[0].name, ''); - await expect(storageModule.put).toHaveBeenCalled(); + await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); + await settingsProvider + .setSettingValue(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name, ''); + await expect(storageProviderSpy.put).toHaveBeenCalled(); }); it('should clear settings', async () => { - const category = SETTING_MOCKS[0].categories[0]; - const name = SETTING_MOCKS[0].name; - await settingsProvider.provideSetting(JSON.parse(JSON.stringify(SETTING_MOCKS[0]))); - await settingsProvider.clear(); - const exists = await settingsProvider.settingExists(category, name); - await expect(exists).toEqual(false); + await settingsProvider.reset(); + await expect(storageProviderSpy.put).toHaveBeenCalledWith(STORAGE_KEY_SETTING_VALUES, {}); }); it('should reset settings', async () => { - const category = SETTING_MOCKS[0].categories[0]; - const name = SETTING_MOCKS[0].name; - await settingsProvider.provideSetting(JSON.parse(JSON.stringify(SETTING_MOCKS[0]))); + const category = CONFIG_SETTINGS_MOCK[0].categories[0]; + const name = CONFIG_SETTINGS_MOCK[0].name; + await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); await settingsProvider.setSettingValue(category, name, 'guest'); await settingsProvider.resetDefault(); - const value = await settingsProvider.getSettingValue(SETTING_MOCKS[0].categories[0], SETTING_MOCKS[0].name); - await expect(value).toEqual(SETTING_MOCKS[0].input.defaultValue); + const value = await settingsProvider + .getValue(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name); + await expect(value).toEqual(CONFIG_SETTINGS_MOCK[0].input.defaultValue); }); it('should validate wrong values for inputType text', async () => { - await testValue(SETTING_MOCKS[0], 123456789); - await testValue(SETTING_MOCKS[0], false); - await testValue(SETTING_MOCKS[0], []); + await testValue(CONFIG_SETTINGS_MOCK[0], 123456789); + await testValue(CONFIG_SETTINGS_MOCK[0], false); + await testValue(CONFIG_SETTINGS_MOCK[0], []); }); it('should validate wrong values for inputType password', async () => { - await testValue(SETTING_MOCKS[0], 123456789); - await testValue(SETTING_MOCKS[0], false); - await testValue(SETTING_MOCKS[0], []); + await testValue(CONFIG_SETTINGS_MOCK[0], 123456789); + await testValue(CONFIG_SETTINGS_MOCK[0], false); + await testValue(CONFIG_SETTINGS_MOCK[0], []); }); it('should validate wrong values for inputType number', async () => { - await testValue(SETTING_MOCKS[2], ''); - await testValue(SETTING_MOCKS[2], false); - await testValue(SETTING_MOCKS[2], []); + await testValue(CONFIG_SETTINGS_MOCK[2], ''); + await testValue(CONFIG_SETTINGS_MOCK[2], false); + await testValue(CONFIG_SETTINGS_MOCK[2], []); }); it('should validate wrong values for inputType singleChoice text', async () => { - await testValue(SETTING_MOCKS[3], ''); - await testValue(SETTING_MOCKS[3], 123456); - await testValue(SETTING_MOCKS[3], false); - await testValue(SETTING_MOCKS[3], []); + await testValue(CONFIG_SETTINGS_MOCK[3], ''); + await testValue(CONFIG_SETTINGS_MOCK[3], 123456); + await testValue(CONFIG_SETTINGS_MOCK[3], false); + await testValue(CONFIG_SETTINGS_MOCK[3], []); }); it('should validate wrong values for inputType singleChoice boolean', async () => { - await testValue(SETTING_MOCKS[5], ''); - await testValue(SETTING_MOCKS[5], 123456); - await testValue(SETTING_MOCKS[5], []); + await testValue(CONFIG_SETTINGS_MOCK[5], ''); + await testValue(CONFIG_SETTINGS_MOCK[5], 123456); + await testValue(CONFIG_SETTINGS_MOCK[5], []); }); it('should validate wrong values for inputType multipleChoice', async () => { - await testValue(SETTING_MOCKS[6], ''); - await testValue(SETTING_MOCKS[6], 123456); - await testValue(SETTING_MOCKS[6], false); - await testValue(SETTING_MOCKS[6], [1, 2, 3, 4]); + await testValue(CONFIG_SETTINGS_MOCK[6], ''); + await testValue(CONFIG_SETTINGS_MOCK[6], 123456); + await testValue(CONFIG_SETTINGS_MOCK[6], false); + await testValue(CONFIG_SETTINGS_MOCK[6], [1, 2, 3, 4]); }); async function testValue(setting: SCSetting, value: any) { @@ -144,10 +186,10 @@ describe('SettingsProvider', () => { // @ts-ignore await expect(error).toBeDefined(); // @ts-ignore - await expect(error.message).toMatch(/Value.*not valid/); + await expect(error.message).toMatch(/is not valid/); } - const SETTING_MOCKS: SCSetting[] = [ + const CONFIG_SETTINGS_MOCK: SCSetting[] = [ { categories: ['credentials'], input: { @@ -354,3 +396,16 @@ describe('SettingsProvider', () => { }, ]; }); + +const SETTING_VALUES_MOCK: SettingValuesContainer = { + foo: { + bar: 'foo-bar', + }, + privacy: { + geoLocation: 'true', + }, + profile: { + group: 'employee', + language: 'de', + }, +}; diff --git a/src/app/modules/settings/settings.provider.ts b/src/app/modules/settings/settings.provider.ts index 72e3ee28..9b325333 100644 --- a/src/app/modules/settings/settings.provider.ts +++ b/src/app/modules/settings/settings.provider.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 StApps + * Copyright (C) 2018, 2019 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. @@ -13,7 +13,16 @@ * this program. If not, see . */ import {Injectable} from '@angular/core'; -import {SCSetting, SCSettingMultipleChoice, SCSettingSingleChoice, SCSettingValue} from '@openstapps/core'; +import {Geolocation} from '@ionic-native/geolocation/ngx'; +import { + SCSetting, + SCSettingMultipleChoice, + SCSettingSingleChoice, + SCSettingValue, +} from '@openstapps/core'; +import {Logger} from '@openstapps/logger'; +import * as deepMerge from 'deepmerge'; +import {ConfigProvider} from '../config/config.provider'; import {StorageProvider} from '../storage/storage.provider'; export const STORAGE_KEY_SETTINGS = 'settings'; @@ -35,14 +44,29 @@ export interface SettingsCache { [key: string]: CategoryWithSettings; } +/** + * Structure with categories and its setting valueContainers for persistence + */ +export interface SettingValuesContainer { + [key: string]: SettingValueContainer; +} + +/** + * Structure of a setting and its value + */ +export interface SettingValueContainer { + [key: string]: SCSettingValue | SCSettingValue[] | undefined; +} + /** * Provider for app settings */ @Injectable() export class SettingsProvider { - private categoriesOrder: string[]; - private initialized = false; - private settingsCache: SettingsCache; + categoriesOrder: string[]; + initialized = false; + logger = new Logger(); + settingsCache: SettingsCache; /** * Return true if all given values are valid to possible values in given settingInput @@ -102,17 +126,56 @@ export class SettingsProvider { return isValueValid; } - constructor(public storage: StorageProvider) { + constructor(private storage: StorageProvider, + private configProvider: ConfigProvider, + private geoLocation: Geolocation) { this.categoriesOrder = []; this.settingsCache = {}; } /** - * Initializes settings cache from storage + * Add an Setting to the Cache if not exist and set undefined value to defaultValue + * @param setting + */ + private async addSetting(setting: SCSetting): Promise { + if (!this.categoryExists(setting.categories[0])) { + await this.provideCategory(setting.categories[0]); + } + if (!this.settingExists(setting.categories[0], setting.name)) { + if (setting.input.value === undefined) { + setting.input.value = setting.input.defaultValue; + } + this.settingsCache[setting.categories[0]].settings[setting.name] = setting; + } + } + + /** + * Returns all setting values from settingsCache in a SettingsValueContainer + */ + private getSettingValuesFromCache(): SettingValuesContainer { + const settingValuesContainer: SettingValuesContainer = {}; + // iterate through keys of categories + for (const categoryKey of Object.keys(this.settingsCache)) { + // iterate through keys of settingValueContainer + for (const settingKey of Object.keys(this.settingsCache[categoryKey].settings)) { + if (typeof settingValuesContainer[categoryKey] === 'undefined') { + settingValuesContainer[categoryKey] = {}; + } + settingValuesContainer[categoryKey][settingKey] = + this.settingsCache[categoryKey].settings[settingKey].input.value; + } + } + return settingValuesContainer; + } + + /** + * Initializes settings from config and stored values if exist */ private async initSettings(): Promise { try { - this.settingsCache = await this.storage.get(STORAGE_KEY_SETTING_VALUES); + const settings: SCSetting[] = (await this.configProvider.getValue('settings')) as SCSetting[]; + settings.forEach((setting) => this.addSetting(setting)); + for (const category of Object.keys(this.settingsCache)) { if (!this.categoriesOrder.includes(category)) { this.categoriesOrder.push(category); @@ -121,7 +184,29 @@ export class SettingsProvider { } catch (error) { this.settingsCache = {}; } - await this.saveSettings(); + + if (await this.storage.has(STORAGE_KEY_SETTING_VALUES)) { + // get setting values from StorageProvider into settingsCache + const valuesContainer: SettingValuesContainer = + await this.storage.get(STORAGE_KEY_SETTING_VALUES); + // iterate through keys of categories + for (const categoryKey of Object.keys(this.settingsCache)) { + // iterate through setting keys of category + for (const settingKey of Object.keys(this.settingsCache[categoryKey].settings)) { + // if saved setting value exists set it, otherwise set to default value + if (typeof valuesContainer[categoryKey] !== 'undefined' + && typeof valuesContainer[categoryKey][settingKey] !== 'undefined') { + this.settingsCache[categoryKey].settings[settingKey].input.value = + valuesContainer[categoryKey][settingKey]; + } else { + this.settingsCache[categoryKey].settings[settingKey].input.value = + this.settingsCache[categoryKey].settings[settingKey].input.defaultValue; + } + } + } + await this.saveSettingValues(); + } + this.initialized = true; } @@ -150,16 +235,40 @@ export class SettingsProvider { } /** - * Removes all provided settings + * Checks for user permission to use location */ - public async clear(): Promise { - await this.init(); - this.settingsCache = {}; - await this.saveSettings(); + public async checkGeoLocationPermission(): Promise { + // request geoLocation to test the user permission + try { + // set enableHighAccuracy, otherwise android platform does not respond + const options = { + enableHighAccuracy: true, + }; + await this.geoLocation.getCurrentPosition(options); + } catch (error) { + // if error code is 1 the user denied permission, + // other errors like 'timeout' or 'no location' will be ignored here + if (error.code === 1) { + // ios has special error message for disabled location services, for the setting we ignore it + if (error.message.toLowerCase() !== 'location services are disabled.') { + // revert setting value + return false; + } + } + } + return true; } /** - * returns an array with the order of categories + * Returns copy of cached settings + */ + public async getCache(): Promise { + await this.init(); + return JSON.parse(JSON.stringify(this.settingsCache)); + } + + /** + * Returns an array with the order of categories */ public getCategoriesOrder(): string[] { return this.categoriesOrder; @@ -175,33 +284,27 @@ export class SettingsProvider { public async getSetting(category: string, name: string): Promise { await this.init(); if (this.settingExists(category, name)) { + // return a copy of the settings return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name])); } else { - throw new Error('Setting "' + name + '" not provided'); + throw new Error(`Setting "${name}" not provided`); } } /** - * Returns copy of cached settings - */ - public async getSettingsCache(): Promise { - await this.init(); - return JSON.parse(JSON.stringify(this.settingsCache)); - } - - /** - * Returns copy of a setting if exist + * Returns copy of a settings value if exist * @param category the category of requested setting * @param name the name of requested setting * * @throws Exception if setting is not provided */ - public async getSettingValue(category: string, name: string): Promise { + public async getValue(category: string, name: string): Promise { await this.init(); if (this.settingExists(category, name)) { + // return a copy of the settings value return JSON.parse(JSON.stringify(this.settingsCache[category].settings[name].input.value)); } else { - throw new Error('Setting "' + name + '" not provided'); + throw new Error(`Setting "${name}" not provided`); } } @@ -220,37 +323,44 @@ export class SettingsProvider { */ public async provideSetting(setting: SCSetting): Promise { await this.init(); - if (!this.categoryExists(setting.categories[0])) { - await this.provideCategory(setting.categories[0]); - } - if (!this.settingExists(setting.categories[0], setting.name)) { - // set value to default - if (setting.input.value === undefined) { - setting.input.value = setting.input.defaultValue; - } - this.settingsCache[setting.categories[0]].settings[setting.name] = setting; - await this.saveSettings(); - } + await this.addSetting(setting); + } + + /** + * Deletes saved values and reinitialising the settings + */ + public async reset(): Promise { + await this.storage.put(STORAGE_KEY_SETTING_VALUES, {}); + await this.initSettings(); } /** * Sets values of all settings to defaultValue */ - public async resetDefault(): Promise { - await this.init(); + async resetDefault(): Promise { for (const catKey of Object.keys(this.settingsCache)) { for (const settingKey of Object.keys(this.settingsCache[catKey].settings)) { const settingInput = this.settingsCache[catKey].settings[settingKey].input; settingInput.value = settingInput.defaultValue; } } + await this.saveSettingValues(); } /** * Saves cached settings in app storage */ - public async saveSettings(): Promise { - await this.storage.put(STORAGE_KEY_SETTING_VALUES, this.settingsCache); + public async saveSettingValues(): Promise { + if (await this.storage.has(STORAGE_KEY_SETTING_VALUES)) { + const savedSettingsValues: SettingValuesContainer = + await this.storage.get(STORAGE_KEY_SETTING_VALUES); + const cacheSettingsValues = this.getSettingValuesFromCache(); + const mergedSettingValues = deepMerge(savedSettingsValues, cacheSettingsValues); + await this.storage + .put(STORAGE_KEY_SETTING_VALUES, mergedSettingValues); + } else { + await this.storage.put(STORAGE_KEY_SETTING_VALUES, this.getSettingValuesFromCache()); + } } /** @@ -269,20 +379,21 @@ export class SettingsProvider { * * @throws Exception if setting is not provided or value not valid to the settings inputType */ - public async setSettingValue(category: string, name: string, value: any): Promise { + public async setSettingValue(category: string, name: string, + value: any): Promise { await this.init(); if (this.settingExists(category, name)) { const setting: SCSetting = this.settingsCache[category].settings[name]; const isValueValid = SettingsProvider.validateValue(setting, value); if (isValueValid) { this.settingsCache[category].settings[name].input.value = value; - await this.saveSettings(); + await this.saveSettingValues(); } else { - throw new Error('Value "' + value + '" of type ' + - typeof value + ' is not valid for ' + setting.input.inputType); - } + throw new Error(`Value "${value}" of type + ${typeof value} is not valid for ${setting.input.inputType}`); + } } else { - throw new Error('setting ' + name + ' is not provided'); + throw new Error(`setting ${name} is not provided`); } } diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 1523d225..a6319ba7 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -16,10 +16,16 @@ } }, "settings": { + "resetAlert.title": "Alle Einstellungen zurücksetzen?", + "resetAlert.message": "Sind Sie sich sicher, alle Einstellungen auf ihre Anfangswerte zurückzusetzen?", + "resetAlert.buttonYes": "Ja", + "resetAlert.buttonCancel": "Abbrechen", + "resetToast.message": "Einstellungen wurden zurückgesetzt", "title": "Einstellungen", "geoLocation": { "permission_denied_title": "Erlaubnis für Ortungsdienst nicht gegeben", "permission_denied_message": "Erlaube der App die Nutzung des Ortungsdienstes, um diese Funktion zu aktivieren." - } + }, + "resetSettings": "Einstellungen zurücksetzen" } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 9e695cc9..e5c20755 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -16,10 +16,16 @@ } }, "settings": { + "resetAlert.title": "Reset all settings?", + "resetAlert.message": "Are you shure to reset all settings to defaults values?", + "resetAlert.buttonYes": "Yes", + "resetAlert.buttonCancel": "Cancel", + "resetToast.message": "Settings reset", "title": "Settings", "geoLocation": { "permission_denied_title": "Location permission not granted", "permission_denied_message": "Allow this app to use location services to activate this feature." - } + }, + "resetSettings": "Reset Settings" } }