diff --git a/.gitignore b/.gitignore
index f4f46a5feeb99832da5739425488cf171397a413..8bd19c97593cdb7b55c66cf2997390a996e13077 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,3 +44,6 @@ testem.log
# System Files
.DS_Store
Thumbs.db
+
+### Sonar ###
+.scannerwork
diff --git a/README.md b/README.md
index e97bd394205cba66e1656d534642347094729b3c..307680872f2e77dc7b3777afceef3c6c0a7753b2 100644
--- a/README.md
+++ b/README.md
@@ -3,26 +3,38 @@
## Building
```
-sudo apt install nodejs
-sudo apt install npm
-sudo npm install -g npm@latest
-sudo npm install -g @angular/cli
-
+sudo apt install nodejs npm
+sudo -H npm install -g npm@latest
+sudo -H npm install -g @angular/cli
git clone
-npm install
+cd
+npm ci
+npm run lint
+npm run test-headless
```
-## Testing locally
+## Sonarqube
+By default `http://localhost:9000` is used as a sonarqube server.
+If you have a remote sonarqube server, update `sonar-project.properties` cunfiguration file and run the test with:
```
-ng serve --host 0.0.0.0
+npm run sonar
+```
+Alternatively you can provide hostname and access token with command line:
+```
+./node_modules/sonar-scanner/bin/sonar-scanner -Dsonar.host.url= -Dsonar.login=
```
-## Update angular
+## Updating angular version
```
ng update @angular/cli @angular/core
```
+## Deploy for local manual testing
+```
+ng serve --host 0.0.0.0
+```
+
## Build for production
```
-ng build --prod --base-href /methods/
+ng build --prod --base-href /catalogue/
```
diff --git a/README_NG.md b/README_NG.md
deleted file mode 100644
index 1555de6058c9ff89f6c352381b54390b8e2ffa34..0000000000000000000000000000000000000000
--- a/README_NG.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Methods
-
-This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.3.2.
-
-## Development server
-
-Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
-
-## Code scaffolding
-
-Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
-
-## Build
-
-Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
-
-## Running unit tests
-
-Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
-
-## Running end-to-end tests
-
-Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
-
-## Further help
-
-To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
diff --git a/angular.json b/angular.json
index e5398d40660876f6365b305efdcc4ccd8f391e08..2fca3ed0f6793f6931a63dcded789629558497e0 100644
--- a/angular.json
+++ b/angular.json
@@ -3,7 +3,7 @@
"version": 1,
"newProjectRoot": "projects",
"projects": {
- "methods": {
+ "xtss-catalogue": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
@@ -13,7 +13,7 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
- "outputPath": "dist/methods",
+ "outputPath": "dist/catalogue",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
@@ -58,23 +58,24 @@
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
- "browserTarget": "methods:build"
+ "browserTarget": "xtss-catalogue:build"
},
"configurations": {
"production": {
- "browserTarget": "methods:build:production"
+ "browserTarget": "xtss-catalogue:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
- "browserTarget": "methods:build"
+ "browserTarget": "xtss-catalogue:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
+ "codeCoverage": true,
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
@@ -103,7 +104,7 @@
}
}
},
- "methods-e2e": {
+ "xtss-catalogue-e2e": {
"root": "e2e/",
"projectType": "application",
"prefix": "",
@@ -112,11 +113,11 @@
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
- "devServerTarget": "methods:serve"
+ "devServerTarget": "xtss-catalogue:serve"
},
"configurations": {
"production": {
- "devServerTarget": "methods:serve:production"
+ "devServerTarget": "xtss-catalogue:serve:production"
}
}
},
@@ -132,5 +133,5 @@
}
}
},
- "defaultProject": "methods"
+ "defaultProject": "xtss-catalogue"
}
\ No newline at end of file
diff --git a/e2e/src/app.e2e-spec.ts b/e2e/src/app.e2e-spec.ts
index a685de4470bd7067d4ea85fb77bd5a8e823ebfe0..700bc8c12ba57c68319c4888661faea2eb4e7fc4 100644
--- a/e2e/src/app.e2e-spec.ts
+++ b/e2e/src/app.e2e-spec.ts
@@ -10,7 +10,7 @@ describe('workspace-project App', () => {
it('should display welcome message', () => {
page.navigateTo();
- expect(page.getTitleText()).toEqual('Welcome to methods!');
+ expect(page.getTitleText()).toEqual('Welcome to xtss-catalogue!');
});
afterEach(async () => {
diff --git a/package-lock.json b/package-lock.json
index 3b719589ca40514bd2ac735a4976c375d386852c..525893fc553ea72f8964a06cf90d486f2cb41534 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,16 +1,16 @@
{
- "name": "methods",
- "version": "0.0.0",
+ "name": "xtss-catalogue",
+ "version": "0.2.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@angular-devkit/architect": {
- "version": "0.13.3",
- "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.3.tgz",
- "integrity": "sha512-89VL75bq3+h3m0jhzWNqXqW+HQcrihnM3i6eiUE6P81LcllP159JMlusAvB1LHLNc6Cc62wTq4BJr7KDILkPOA==",
+ "version": "0.13.8",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.8.tgz",
+ "integrity": "sha512-gxUs5rhnP576T8ZclKqxlspiChrqRtqaJo54wqNVFvYKEjRZKyMa+1AK6p0oD9zcIToEkcjknj3BbtQa27lLHg==",
"dev": true,
"requires": {
- "@angular-devkit/core": "7.3.3",
+ "@angular-devkit/core": "7.3.8",
"rxjs": "6.3.3"
},
"dependencies": {
@@ -26,16 +26,16 @@
}
},
"@angular-devkit/build-angular": {
- "version": "0.13.3",
- "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.13.3.tgz",
- "integrity": "sha512-UxD6UR/tXypMA4lqCiXLtcStI4wuIHLOJLwADmazndFjg1oLqH1onO6UQPHJ1drAUl+AzA5zTQZHzWYokxaLtg==",
+ "version": "0.13.8",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.13.8.tgz",
+ "integrity": "sha512-uRb8CKC0hUdcE+Fv2Ov9LJNelyjsiMuddBpo8pdTKCIHVVC6hvip9S/Z18Tvb207kKI3k7Dn+Ji1J63mCqmQzA==",
"dev": true,
"requires": {
- "@angular-devkit/architect": "0.13.3",
- "@angular-devkit/build-optimizer": "0.13.3",
- "@angular-devkit/build-webpack": "0.13.3",
- "@angular-devkit/core": "7.3.3",
- "@ngtools/webpack": "7.3.3",
+ "@angular-devkit/architect": "0.13.8",
+ "@angular-devkit/build-optimizer": "0.13.8",
+ "@angular-devkit/build-webpack": "0.13.8",
+ "@angular-devkit/core": "7.3.8",
+ "@ngtools/webpack": "7.3.8",
"ajv": "6.9.1",
"autoprefixer": "9.4.6",
"circular-dependency-plugin": "5.0.2",
@@ -52,7 +52,7 @@
"mini-css-extract-plugin": "0.5.0",
"minimatch": "3.0.4",
"node-sass": "4.11.0",
- "opn": "5.4.0",
+ "open": "6.0.0",
"parse5": "4.0.0",
"postcss": "7.0.14",
"postcss-import": "12.0.1",
@@ -63,7 +63,7 @@
"semver": "5.6.0",
"source-map-loader": "0.2.4",
"source-map-support": "0.5.10",
- "speed-measure-webpack-plugin": "1.3.0",
+ "speed-measure-webpack-plugin": "1.3.1",
"stats-webpack-plugin": "0.7.0",
"style-loader": "0.23.1",
"stylus": "0.54.5",
@@ -90,9 +90,9 @@
}
},
"@angular-devkit/build-optimizer": {
- "version": "0.13.3",
- "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.13.3.tgz",
- "integrity": "sha512-lxM1icVFy3jyoQfWEGW8TG1M7LTl/Djc98MFBYp/lXoVo2JZoLxy7eo51sRuJFaB7/0mgMP2gs0FcU/Lr4gK+Q==",
+ "version": "0.13.8",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.13.8.tgz",
+ "integrity": "sha512-RvYxtsdYuvpFb1iivVixylSVN/Q8LsQ449uYuqEe3OsDjQBvUVG2fMLPOQjmKWhi0NC9WSsNiUluxLDNdvd0Vw==",
"dev": true,
"requires": {
"loader-utils": "1.2.3",
@@ -110,13 +110,13 @@
}
},
"@angular-devkit/build-webpack": {
- "version": "0.13.3",
- "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.13.3.tgz",
- "integrity": "sha512-o2ymctVCuz5GhKJH3LO1sl3AUbA4j7zlrqSGB5ToVRBn3GckJJnmfCZzr2SX5Ya4VofxVsIidsiZcawy4FpB2w==",
+ "version": "0.13.8",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.13.8.tgz",
+ "integrity": "sha512-WMyn1vUHyx+VfJKgYuEHrICwQzPMDTaUNB1zlvzZt9gX/9H+XnetrebeWBZCITPXHBw/377oA6wmiHWJ0yaZRw==",
"dev": true,
"requires": {
- "@angular-devkit/architect": "0.13.3",
- "@angular-devkit/core": "7.3.3",
+ "@angular-devkit/architect": "0.13.8",
+ "@angular-devkit/core": "7.3.8",
"rxjs": "6.3.3"
},
"dependencies": {
@@ -132,9 +132,9 @@
}
},
"@angular-devkit/core": {
- "version": "7.3.3",
- "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
- "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.8.tgz",
+ "integrity": "sha512-3X9uzaZXFpm5o2TSzhD6wEOtVU32CgeytKjD1Scxj+uMMVo48SWLlKiFh312T+smI9ko7tOT8VqxglwYkWosgg==",
"dev": true,
"requires": {
"ajv": "6.9.1",
@@ -156,19 +156,19 @@
}
},
"@angular-devkit/schematics": {
- "version": "7.3.3",
- "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.3.3.tgz",
- "integrity": "sha512-SdDq9eKwceb6WLwci1fywtZ/kARR5CYyzi5dZIR1lOxrz00682uUBqH/X39mKdqc6eVqR7rtPceqNm6nQpOIMg==",
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.3.8.tgz",
+ "integrity": "sha512-mvaKoORZIaW/h0VNZ3IQWP0qThRCZRX6869FNlzV0jlW0mhn07XbiIGHCGGSCDRxS7qJ0VbuIVnKXntF+iDeWw==",
"dev": true,
"requires": {
- "@angular-devkit/core": "7.3.3",
+ "@angular-devkit/core": "7.3.8",
"rxjs": "6.3.3"
},
"dependencies": {
"@angular-devkit/core": {
- "version": "7.3.3",
- "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
- "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.8.tgz",
+ "integrity": "sha512-3X9uzaZXFpm5o2TSzhD6wEOtVU32CgeytKjD1Scxj+uMMVo48SWLlKiFh312T+smI9ko7tOT8VqxglwYkWosgg==",
"dev": true,
"requires": {
"ajv": "6.9.1",
@@ -190,48 +190,48 @@
}
},
"@angular/animations": {
- "version": "7.2.7",
- "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.2.7.tgz",
- "integrity": "sha512-eU/wSkBmukZXCCe/epUl02xsKPauF+deMbncxBE+w/NmmWjJ77Q09iZAcgzM92RVXj2LsVYQXsNEBGT3X0hRZw==",
+ "version": "7.2.13",
+ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.2.13.tgz",
+ "integrity": "sha512-Z0g0DthJnxTZ0dUc5BlojMq/0XIikhWzTqq0ym8w3G6jqBJD0OJ0jRCIfV0Leqlgzq6Jzvdrx0/JngBiKi5+uA==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/cli": {
- "version": "7.3.3",
- "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.3.3.tgz",
- "integrity": "sha512-dw1iBOYbQRN2l/BH21zDItDFC9KXgqeK0A/koDLDukjrUAnW/XVATjxGi+7EPlTpABTFhqu/rHZDy8aBglLDXQ==",
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.3.8.tgz",
+ "integrity": "sha512-5ldU1idvWstmRaavGZen9WRjfjIViERGt8NYuLLI7dgVLYOPF5TyFoTnpT5nxkiCopp4tPIcpbzPV394Bxmdtg==",
"dev": true,
"requires": {
- "@angular-devkit/architect": "0.13.3",
- "@angular-devkit/core": "7.3.3",
- "@angular-devkit/schematics": "7.3.3",
- "@schematics/angular": "7.3.3",
- "@schematics/update": "0.13.3",
+ "@angular-devkit/architect": "0.13.8",
+ "@angular-devkit/core": "7.3.8",
+ "@angular-devkit/schematics": "7.3.8",
+ "@schematics/angular": "7.3.8",
+ "@schematics/update": "0.13.8",
"@yarnpkg/lockfile": "1.1.0",
"ini": "1.3.5",
"inquirer": "6.2.1",
"npm-package-arg": "6.1.0",
- "opn": "5.4.0",
+ "open": "6.0.0",
"pacote": "9.4.0",
"semver": "5.6.0",
"symbol-observable": "1.2.0"
},
"dependencies": {
"@angular-devkit/architect": {
- "version": "0.13.3",
- "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.3.tgz",
- "integrity": "sha512-89VL75bq3+h3m0jhzWNqXqW+HQcrihnM3i6eiUE6P81LcllP159JMlusAvB1LHLNc6Cc62wTq4BJr7KDILkPOA==",
+ "version": "0.13.8",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.8.tgz",
+ "integrity": "sha512-gxUs5rhnP576T8ZclKqxlspiChrqRtqaJo54wqNVFvYKEjRZKyMa+1AK6p0oD9zcIToEkcjknj3BbtQa27lLHg==",
"dev": true,
"requires": {
- "@angular-devkit/core": "7.3.3",
+ "@angular-devkit/core": "7.3.8",
"rxjs": "6.3.3"
}
},
"@angular-devkit/core": {
- "version": "7.3.3",
- "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
- "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.8.tgz",
+ "integrity": "sha512-3X9uzaZXFpm5o2TSzhD6wEOtVU32CgeytKjD1Scxj+uMMVo48SWLlKiFh312T+smI9ko7tOT8VqxglwYkWosgg==",
"dev": true,
"requires": {
"ajv": "6.9.1",
@@ -253,25 +253,25 @@
}
},
"@angular/common": {
- "version": "7.2.7",
- "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.7.tgz",
- "integrity": "sha512-U1l2CIcmpTAJMWcyTXI9qt1E8CxwKNW1vr6XWZo4X5ziCIzf7RvClzK7Ci5KZKkoPJrJqBJu661Q75Yt22dJsg==",
+ "version": "7.2.13",
+ "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.13.tgz",
+ "integrity": "sha512-NYlzUkFVgjLg9VB6/lkd8ZV0ZezSiv9vlg+26wOyw7x+gahRrm5WMAGF7eBLrXoZPEaoOO0uhKWKo7oiA0aufA==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/compiler": {
- "version": "7.2.7",
- "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.7.tgz",
- "integrity": "sha512-e61YVxW5x4w+X4yjGaptYoJIja7HwH0+8FFEaH6VuPl/DrK8wP4HDMhLo4NzdgeZKLR2jBIQSqLmoM8W7UXcqw==",
+ "version": "7.2.13",
+ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.13.tgz",
+ "integrity": "sha512-k0IvaozNIlrPKUNF3M/NXMb/jfHBCDO9uRYA6h+84FFY4Y9po40c7YXfsfUxGKwouTWyemaxy9iXlLEnd3ELSQ==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/compiler-cli": {
- "version": "7.2.7",
- "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.7.tgz",
- "integrity": "sha512-UPWROJzBLejgNf+aqgEUXYts8UiFOl2IavDhS/olA9irszv2lNFj9Yqr8OKdy0jK/lKaipZog3VZEx8g5dNeBA==",
+ "version": "7.2.13",
+ "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.13.tgz",
+ "integrity": "sha512-UpA6V+GCY9qKj5j6tvzun2DJNjqRKjCrQgJqD5BIf4FTAKjVgqOvh++d23tbdltdjXlbHqUVRgfeXltbO91fWg==",
"dev": true,
"requires": {
"canonical-path": "1.0.0",
@@ -300,9 +300,9 @@
"dev": true
},
"chokidar": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.2.tgz",
- "integrity": "sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==",
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz",
+ "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==",
"dev": true,
"requires": {
"anymatch": "^2.0.0",
@@ -316,7 +316,7 @@
"normalize-path": "^3.0.0",
"path-is-absolute": "^1.0.0",
"readdirp": "^2.2.1",
- "upath": "^1.1.0"
+ "upath": "^1.1.1"
}
},
"cross-spawn": {
@@ -456,6 +456,12 @@
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
},
+ "upath": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz",
+ "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==",
+ "dev": true
+ },
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
@@ -501,47 +507,47 @@
}
},
"@angular/core": {
- "version": "7.2.7",
- "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.7.tgz",
- "integrity": "sha512-E7qjMQdS77SbRROKu13VsfL+MJN52eTlrU0SzEAFGUOgdvbmDYJOaEwjqrouKpYZ0pul8KOoalvlPB7oVflC7A==",
+ "version": "7.2.13",
+ "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.13.tgz",
+ "integrity": "sha512-vHD69xxDDSQaE8KfHeY2STJSd3xgfsz3/meBCAnT+Bpq9LqxL8DuPlrkC0kyBa2vyj/BwPR3CJNTaQrZcszJ/w==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/forms": {
- "version": "7.2.7",
- "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.7.tgz",
- "integrity": "sha512-2gBs+BG2cMPsHq9JVEzmu2Ev539zjfHmr6cna2W38KLXeGbNf42rbbMUXpYD8cndY0QTYcnwfMpRNIl9zKRZnw==",
+ "version": "7.2.13",
+ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.13.tgz",
+ "integrity": "sha512-dBz7kYa8XoCKxZ+3EvYt6CxHZhM9Qbn3uYkLMsPA+NC6GtIt/tmYn1kNn+YWgVWZtWLvYRaOtYiCuMUJaRNQQw==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/language-service": {
- "version": "7.2.7",
- "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.7.tgz",
- "integrity": "sha512-d3iCBpOfgLNSGMrtqZvN6NHZIEnKD2MV8Hz4WsRLU4WY0RbshZj5dqx2nO3YRT2tACpAvhWBQoYvtLpTCPzsMA==",
+ "version": "7.2.13",
+ "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.13.tgz",
+ "integrity": "sha512-1bNWJpwH9wB0JybkbjdQp9J4bGmGxJX6BG7Mz3188Wc4J+aNy696Gc6IaJs7tFK8VXAdJrTJ5jGr9Oiu+ATe8w==",
"dev": true
},
"@angular/platform-browser": {
- "version": "7.2.7",
- "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.7.tgz",
- "integrity": "sha512-9C3ffZs0ZUw+dYg1oJKiONf64UKTdAzIOaTQXTrVrCa3oN7Jb2tUfmpenmB+ATRxwhL2n7Yi725YWwxY2FwqvQ==",
+ "version": "7.2.13",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.13.tgz",
+ "integrity": "sha512-4n9De4sOwVoYHh6IGO2+UQIjABqGAXk4RdrEGpXqPBHCNO4sF43c2JsXbPTU4kjPVwTwposfLlKEOjTXfwxGow==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/platform-browser-dynamic": {
- "version": "7.2.7",
- "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.7.tgz",
- "integrity": "sha512-3nlcwCZOzlKw/4CMJ4zy1JEVy8Ky4KyLRRePLledOMdsGbuDIoq/kyAnBzg295Xe9ovBxv8cmuSkShci+s/x8g==",
+ "version": "7.2.13",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.13.tgz",
+ "integrity": "sha512-3+/BzrNLQ/Tn1hoPal3fvIeB3S/P3e00gHcH3oK+hfACYgWxLE1oIHL+w4NE2eTIJbHfphKhuascMaOH5WNlkg==",
"requires": {
"tslib": "^1.9.0"
}
},
"@angular/router": {
- "version": "7.2.7",
- "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.7.tgz",
- "integrity": "sha512-59+M8+IH7V2NPPqWw2mwdg+kh/jfwQcXE0tB8iZ5V2ldACPucY/Td6qiT5H6t7EkELtvkKJwS6vKFV22qdRp3w==",
+ "version": "7.2.13",
+ "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.13.tgz",
+ "integrity": "sha512-pTdJT9TXk1A9YMa6C2zRRqLB4GPGMSik838P7n+yGrzhdybiudZU9T3egcxDRCWQMjsobVBRKLEUn405n3Hjgg==",
"requires": {
"tslib": "^1.9.0"
}
@@ -707,12 +713,12 @@
}
},
"@ngtools/webpack": {
- "version": "7.3.3",
- "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.3.3.tgz",
- "integrity": "sha512-G/1P00XHWVrKT3qoSyy7yAPT5/fuja84YifcGg/2SwmNNo4hTXxWhqec0/uHwgQr6nYhGDyzwwXYeKKyQkcfgw==",
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.3.8.tgz",
+ "integrity": "sha512-gfjSKz+F/2T4tZHpnQ1XqelKP/CIfI87XdoHsOI53ceTUrAkVKsOb3ULmEfkcdsdQZ/HhmCiLivcutHcW8xkhQ==",
"dev": true,
"requires": {
- "@angular-devkit/core": "7.3.3",
+ "@angular-devkit/core": "7.3.8",
"enhanced-resolve": "4.1.0",
"rxjs": "6.3.3",
"tree-kill": "1.2.1",
@@ -747,20 +753,20 @@
}
},
"@schematics/angular": {
- "version": "7.3.3",
- "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.3.tgz",
- "integrity": "sha512-HbH8vajYPka0xGcFAN5IUBx8n8SFMQLFb9di2dJCOBaEakbKVkk8qtOpil54oFQbx7DFCvutq/p0u42JfEbuMQ==",
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.8.tgz",
+ "integrity": "sha512-7o90bnIxXNpJhWPDY/zCedcG6KMIihz7a4UQe6UdlhEX21MNZLYFiDiR5Vmsx39wjm2EfPh3JTuBIHGmMCXkQQ==",
"dev": true,
"requires": {
- "@angular-devkit/core": "7.3.3",
- "@angular-devkit/schematics": "7.3.3",
+ "@angular-devkit/core": "7.3.8",
+ "@angular-devkit/schematics": "7.3.8",
"typescript": "3.2.4"
},
"dependencies": {
"@angular-devkit/core": {
- "version": "7.3.3",
- "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
- "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.8.tgz",
+ "integrity": "sha512-3X9uzaZXFpm5o2TSzhD6wEOtVU32CgeytKjD1Scxj+uMMVo48SWLlKiFh312T+smI9ko7tOT8VqxglwYkWosgg==",
"dev": true,
"requires": {
"ajv": "6.9.1",
@@ -782,13 +788,13 @@
}
},
"@schematics/update": {
- "version": "0.13.3",
- "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.13.3.tgz",
- "integrity": "sha512-sCOFQ62dd7VdEGiSUJNZshNI31ODwpJjn2WIvFgZLt6sdHHun67s/JOvOUq4mxx6I74oD6RPJPF4AP5sigVxxg==",
+ "version": "0.13.8",
+ "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.13.8.tgz",
+ "integrity": "sha512-2jP9w7Nnn24jOdrJtWjoS9LsNPmO9/Eu/+gDxBAVERCqR71mtNW+DopgWDtxleE9jri/pZWrHwShGFCSS7w23g==",
"dev": true,
"requires": {
- "@angular-devkit/core": "7.3.3",
- "@angular-devkit/schematics": "7.3.3",
+ "@angular-devkit/core": "7.3.8",
+ "@angular-devkit/schematics": "7.3.8",
"@yarnpkg/lockfile": "1.1.0",
"ini": "1.3.5",
"pacote": "9.4.0",
@@ -798,9 +804,9 @@
},
"dependencies": {
"@angular-devkit/core": {
- "version": "7.3.3",
- "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
- "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.8.tgz",
+ "integrity": "sha512-3X9uzaZXFpm5o2TSzhD6wEOtVU32CgeytKjD1Scxj+uMMVo48SWLlKiFh312T+smI9ko7tOT8VqxglwYkWosgg==",
"dev": true,
"requires": {
"ajv": "6.9.1",
@@ -1169,9 +1175,9 @@
"dev": true
},
"ansi-colors": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
- "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
+ "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
"dev": true
},
"ansi-escapes": {
@@ -1283,12 +1289,6 @@
"integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
"dev": true
},
- "array-slice": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
- "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=",
- "dev": true
- },
"array-union": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
@@ -1894,14 +1894,14 @@
}
},
"browserslist": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.2.tgz",
- "integrity": "sha512-ISS/AIAiHERJ3d45Fz0AVYKkgcy+F/eJHzKEvv1j0wwKGKD9T3BrwKr/5g45L+Y4XIK5PlTqefHciRFcfE1Jxg==",
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.4.tgz",
+ "integrity": "sha512-rAjx494LMjqKnMPhFkuLmLp8JWEX0o8ADTGeAbOqaF+XCvYLreZrG5uVjnPBlAQ8REZK4pzXGvp0bWgrFtKaag==",
"dev": true,
"requires": {
- "caniuse-lite": "^1.0.30000939",
- "electron-to-chromium": "^1.3.113",
- "node-releases": "^1.1.8"
+ "caniuse-lite": "^1.0.30000955",
+ "electron-to-chromium": "^1.3.122",
+ "node-releases": "^1.1.13"
}
},
"browserstack": {
@@ -2051,9 +2051,9 @@
}
},
"caniuse-lite": {
- "version": "1.0.30000939",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000939.tgz",
- "integrity": "sha512-oXB23ImDJOgQpGjRv1tCtzAvJr4/OvrHi5SO2vUgB0g0xpdZZoA/BxfImiWfdwoYdUTtQrPsXsvYU/dmCSM8gg==",
+ "version": "1.0.30000960",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000960.tgz",
+ "integrity": "sha512-7nK5qs17icQaX6V3/RYrJkOsZyRNnroA4+ZwxaKJzIKy+crIy0Mz5CBlLySd2SNV+4nbUZeqeNfiaEieUBu3aA==",
"dev": true
},
"canonical-path": {
@@ -2148,12 +2148,6 @@
"integrity": "sha512-oC7/DVAyfcY3UWKm0sN/oVoDedQDQiw/vIiAnuTWTpE5s0zWf7l3WY417Xw/Fbi/QbAjctAkxgMiS9P0s3zkmA==",
"dev": true
},
- "circular-json": {
- "version": "0.5.9",
- "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz",
- "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==",
- "dev": true
- },
"class-utils": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@@ -2309,15 +2303,6 @@
"integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
"dev": true
},
- "combine-lists": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz",
- "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=",
- "dev": true,
- "requires": {
- "lodash": "^4.5.0"
- }
- },
"combined-stream": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
@@ -2373,16 +2358,16 @@
}
},
"compression": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz",
- "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==",
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+ "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
"dev": true,
"requires": {
"accepts": "~1.3.5",
"bytes": "3.0.0",
- "compressible": "~2.0.14",
+ "compressible": "~2.0.16",
"debug": "2.6.9",
- "on-headers": "~1.0.1",
+ "on-headers": "~1.0.2",
"safe-buffer": "5.1.2",
"vary": "~1.1.2"
}
@@ -2703,9 +2688,9 @@
}
},
"date-format": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz",
- "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.0.0.tgz",
+ "integrity": "sha512-M6UqVvZVgFYqZL1SfHsRGIQSz3ZL+qgbsV5Lp1Vj61LZVYuEwcMXYay7DRDtYs2HQQBK5hQtQ0fD9aEJ89V0LA==",
"dev": true
},
"date-now": {
@@ -3006,9 +2991,9 @@
"dev": true
},
"electron-to-chromium": {
- "version": "1.3.113",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz",
- "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==",
+ "version": "1.3.124",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.124.tgz",
+ "integrity": "sha512-glecGr/kFdfeXUHOHAWvGcXrxNU+1wSO/t5B23tT1dtlvYB26GY8aHzZSWD7HqhqC800Lr+w/hQul6C5AF542w==",
"dev": true
},
"elliptic": {
@@ -3193,9 +3178,9 @@
"dev": true
},
"eslint-scope": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
- "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==",
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+ "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
@@ -3302,34 +3287,6 @@
"integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
"dev": true
},
- "expand-braces": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz",
- "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=",
- "dev": true,
- "requires": {
- "array-slice": "^0.2.3",
- "array-unique": "^0.2.1",
- "braces": "^0.1.2"
- },
- "dependencies": {
- "array-unique": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
- "dev": true
- },
- "braces": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz",
- "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=",
- "dev": true,
- "requires": {
- "expand-range": "^0.1.0"
- }
- }
- }
- },
"expand-brackets": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
@@ -3365,30 +3322,6 @@
}
}
},
- "expand-range": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz",
- "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=",
- "dev": true,
- "requires": {
- "is-number": "^0.1.1",
- "repeat-string": "^0.2.2"
- },
- "dependencies": {
- "is-number": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz",
- "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=",
- "dev": true
- },
- "repeat-string": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz",
- "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=",
- "dev": true
- }
- }
- },
"express": {
"version": "4.16.4",
"resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
@@ -3789,6 +3722,17 @@
"null-check": "^1.0.0"
}
},
+ "fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
"fs-minipass": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
@@ -4524,12 +4468,12 @@
"dev": true
},
"handlebars": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz",
- "integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==",
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
+ "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==",
"dev": true,
"requires": {
- "async": "^2.5.0",
+ "neo-async": "^2.6.0",
"optimist": "^0.6.1",
"source-map": "^0.6.1",
"uglify-js": "^3.1.4"
@@ -4828,9 +4772,9 @@
}
},
"ieee754": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
- "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==",
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
"dev": true
},
"iferr": {
@@ -4915,9 +4859,9 @@
}
},
"p-limit": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
- "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
+ "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
@@ -4933,9 +4877,9 @@
}
},
"p-try": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
- "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"pkg-dir": {
@@ -5055,18 +4999,18 @@
}
},
"strip-ansi": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz",
- "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
- "ansi-regex": "^4.0.0"
+ "ansi-regex": "^4.1.0"
},
"dependencies": {
"ansi-regex": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
- "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
}
}
@@ -5615,9 +5559,9 @@
"dev": true
},
"js-yaml": {
- "version": "3.12.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
- "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
@@ -5675,6 +5619,15 @@
"minimist": "^1.2.0"
}
},
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
"jsonparse": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
@@ -5747,28 +5700,27 @@
}
},
"karma": {
- "version": "3.1.4",
- "resolved": "https://registry.npmjs.org/karma/-/karma-3.1.4.tgz",
- "integrity": "sha512-31Vo8Qr5glN+dZEVIpnPCxEGleqE0EY6CtC2X9TagRV3rRQ3SNrvfhddICkJgUK3AgqpeKSZau03QumTGhGoSw==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/karma/-/karma-4.0.1.tgz",
+ "integrity": "sha512-ind+4s03BqIXas7ZmraV3/kc5+mnqwCd+VDX1FndS6jxbt03kQKX2vXrWxNLuCjVYmhMwOZosAEKMM0a2q7w7A==",
"dev": true,
"requires": {
"bluebird": "^3.3.0",
"body-parser": "^1.16.1",
+ "braces": "^2.3.2",
"chokidar": "^2.0.3",
"colors": "^1.1.0",
- "combine-lists": "^1.0.0",
"connect": "^3.6.0",
"core-js": "^2.2.0",
"di": "^0.0.1",
"dom-serialize": "^2.2.0",
- "expand-braces": "^0.1.1",
"flatted": "^2.0.0",
"glob": "^7.1.1",
"graceful-fs": "^4.1.2",
"http-proxy": "^1.13.0",
"isbinaryfile": "^3.0.0",
- "lodash": "^4.17.5",
- "log4js": "^3.0.0",
+ "lodash": "^4.17.11",
+ "log4js": "^4.0.0",
"mime": "^2.3.1",
"minimatch": "^3.0.2",
"optimist": "^0.6.1",
@@ -5783,9 +5735,9 @@
},
"dependencies": {
"mime": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
- "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz",
+ "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==",
"dev": true
},
"source-map": {
@@ -6006,22 +5958,22 @@
"dev": true
},
"log4js": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz",
- "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.1.0.tgz",
+ "integrity": "sha512-eDa+zZPeVEeK6QGJAePyXM6pg4P3n3TO5rX9iZMVY48JshsTyLJZLIL5HipI1kQ2qLsSyOpUqNND/C5H4WhhiA==",
"dev": true,
"requires": {
- "circular-json": "^0.5.5",
- "date-format": "^1.2.0",
- "debug": "^3.1.0",
+ "date-format": "^2.0.0",
+ "debug": "^4.1.1",
+ "flatted": "^2.0.0",
"rfdc": "^1.1.2",
- "streamroller": "0.7.0"
+ "streamroller": "^1.0.4"
},
"dependencies": {
"debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
@@ -6241,14 +6193,22 @@
"dev": true
},
"mem": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz",
- "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
+ "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
"dev": true,
"requires": {
"map-age-cleaner": "^0.1.1",
- "mimic-fn": "^1.0.0",
+ "mimic-fn": "^2.0.0",
"p-is-promise": "^2.0.0"
+ },
+ "dependencies": {
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
+ }
}
},
"memory-fs": {
@@ -6663,9 +6623,9 @@
}
},
"node-releases": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.8.tgz",
- "integrity": "sha512-gQm+K9mGCiT/NXHy+V/ZZS1N/LOaGGqRAAJJs3X9Ah1g+CIbRcBgNyoNYQ+SEtcyAtB9KqDruu+fF7nWjsqRaA==",
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.14.tgz",
+ "integrity": "sha512-d58EpVZRhQE60kWiWUaaPlK9dyC4zg3ZoMcHcky2d4hDksyQj0rUozwInOl0C66mBsqo01Tuns8AvxnL5S7PKg==",
"dev": true,
"requires": {
"semver": "^5.3.0"
@@ -6965,10 +6925,19 @@
"mimic-fn": "^1.0.0"
}
},
+ "open": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-6.0.0.tgz",
+ "integrity": "sha512-/yb5mVZBz7mHLySMiSj2DcLtMBbFPJk5JBKEkHVZFxZAPzeg3L026O0T+lbdz1B2nyDnkClRSwRQJdeVUIF7zw==",
+ "dev": true,
+ "requires": {
+ "is-wsl": "^1.1.0"
+ }
+ },
"opn": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz",
- "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
+ "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==",
"dev": true,
"requires": {
"is-wsl": "^1.1.0"
@@ -7052,9 +7021,9 @@
"dev": true
},
"p-is-promise": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz",
- "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
+ "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
"dev": true
},
"p-limit": {
@@ -7754,9 +7723,9 @@
"dev": true
},
"querystringify": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz",
- "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz",
+ "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==",
"dev": true
},
"randombytes": {
@@ -8771,9 +8740,9 @@
}
},
"socks": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.3.tgz",
- "integrity": "sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA==",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.2.tgz",
+ "integrity": "sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ==",
"dev": true,
"requires": {
"ip": "^1.1.5",
@@ -8781,15 +8750,21 @@
}
},
"socks-proxy-agent": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz",
- "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz",
+ "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==",
"dev": true,
"requires": {
- "agent-base": "~4.2.0",
- "socks": "~2.2.0"
+ "agent-base": "~4.2.1",
+ "socks": "~2.3.2"
}
},
+ "sonar-scanner": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/sonar-scanner/-/sonar-scanner-3.1.0.tgz",
+ "integrity": "sha1-UcHBEB9UuYq8XYVlIJsdkjKXk0M=",
+ "dev": true
+ },
"source-list-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
@@ -8947,9 +8922,9 @@
"dev": true
},
"readable-stream": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz",
- "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz",
+ "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
@@ -8960,9 +8935,9 @@
}
},
"speed-measure-webpack-plugin": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.0.tgz",
- "integrity": "sha512-b9Yd0TrzceMVYSbuamM1sFsGM1oVfyFTM22gOoyLhymNvBVApuYpkdFOgYkKJpN/KhTpcCYcTGHg7X+FJ33Vvw==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.1.tgz",
+ "integrity": "sha512-qVIkJvbtS9j/UeZumbdfz0vg+QfG/zxonAjzefZrqzkr7xOncLVXkeGbTpzd1gjCBM4PmVNkWlkeTVhgskAGSQ==",
"dev": true,
"requires": {
"chalk": "^2.0.1"
@@ -9095,15 +9070,16 @@
"dev": true
},
"streamroller": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz",
- "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.4.tgz",
+ "integrity": "sha512-Wc2Gm5ygjSX8ZpW9J7Y9FwiSzTlKSvcl0FTTMd3rn7RoxDXpBW+xD9TY5sWL2n0UR61COB0LG1BQvN6nTUQbLQ==",
"dev": true,
"requires": {
- "date-format": "^1.2.0",
+ "async": "^2.6.1",
+ "date-format": "^2.0.0",
"debug": "^3.1.0",
- "mkdirp": "^0.5.1",
- "readable-stream": "^2.3.0"
+ "fs-extra": "^7.0.0",
+ "lodash": "^4.17.10"
},
"dependencies": {
"debug": {
@@ -9254,9 +9230,9 @@
"dev": true
},
"tapable": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz",
- "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
"dev": true
},
"tar": {
@@ -9272,16 +9248,22 @@
}
},
"terser": {
- "version": "3.16.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-3.16.1.tgz",
- "integrity": "sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==",
+ "version": "3.17.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz",
+ "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==",
"dev": true,
"requires": {
- "commander": "~2.17.1",
+ "commander": "^2.19.0",
"source-map": "~0.6.1",
- "source-map-support": "~0.5.9"
+ "source-map-support": "~0.5.10"
},
"dependencies": {
+ "commander": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+ "dev": true
+ },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -9329,13 +9311,13 @@
}
},
"find-cache-dir": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.0.0.tgz",
- "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+ "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
"dev": true,
"requires": {
"commondir": "^1.0.1",
- "make-dir": "^1.0.0",
+ "make-dir": "^2.0.0",
"pkg-dir": "^3.0.0"
}
},
@@ -9367,6 +9349,16 @@
"yallist": "^3.0.2"
}
},
+ "make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "requires": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ }
+ },
"mississippi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
@@ -9386,9 +9378,9 @@
}
},
"p-limit": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
- "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
+ "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
@@ -9404,9 +9396,15 @@
}
},
"p-try": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
- "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true
},
"pkg-dir": {
@@ -9770,6 +9768,12 @@
"imurmurhash": "^0.1.4"
}
},
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true
+ },
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -9856,9 +9860,9 @@
}
},
"url-parse": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz",
- "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==",
+ "version": "1.4.6",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.6.tgz",
+ "integrity": "sha512-/B8AD9iQ01seoXmXf9z/MjLZQIdOoYl/+gvsQF6+mpnxaTfG9P7srYaiqaDMyKkR36XMXfhqSHss5MyFAO8lew==",
"dev": true,
"requires": {
"querystringify": "^2.0.0",
@@ -10073,9 +10077,9 @@
},
"dependencies": {
"mime": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
- "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz",
+ "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==",
"dev": true
}
}
@@ -10248,9 +10252,9 @@
}
},
"mime": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
- "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz",
+ "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==",
"dev": true
},
"ms": {
@@ -10271,9 +10275,9 @@
}
},
"p-limit": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
- "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
+ "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
@@ -10289,9 +10293,9 @@
}
},
"p-try": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
- "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"pump": {
diff --git a/package.json b/package.json
index 470de6e947ed6f171f1b5c2a48b63eb35cb11812..2f26860726a6fe76b634102aae0be5f74119d650 100644
--- a/package.json
+++ b/package.json
@@ -1,24 +1,27 @@
{
- "name": "methods",
- "version": "0.0.0",
+ "name": "xtss-catalogue",
+ "version": "0.2.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
+ "test-headless": "ng test --watch=false --browsers=ChromiumHeadless",
+ "test-docker": "ng test --watch=false --browsers=ChromiumDocker",
"lint": "ng lint",
- "e2e": "ng e2e"
+ "e2e": "ng e2e",
+ "sonar": "sonar-scanner"
},
"private": true,
"dependencies": {
- "@angular/animations": "^7.2.7",
- "@angular/common": "^7.2.7",
- "@angular/compiler": "^7.2.7",
- "@angular/core": "^7.2.7",
- "@angular/forms": "^7.2.7",
- "@angular/platform-browser": "^7.2.7",
- "@angular/platform-browser-dynamic": "^7.2.7",
- "@angular/router": "^7.2.7",
+ "@angular/animations": "^7.2.13",
+ "@angular/common": "^7.2.13",
+ "@angular/compiler": "^7.2.13",
+ "@angular/core": "^7.2.13",
+ "@angular/forms": "^7.2.13",
+ "@angular/platform-browser": "^7.2.13",
+ "@angular/platform-browser-dynamic": "^7.2.13",
+ "@angular/router": "^7.2.13",
"@ngx-translate/core": "^11.0.1",
"@ngx-translate/http-loader": "^4.0.0",
"bootstrap": "^4.3.1",
@@ -28,22 +31,23 @@
"zone.js": "~0.8.26"
},
"devDependencies": {
- "@angular-devkit/build-angular": "^0.13.3",
- "@angular/cli": "~7.3.3",
- "@angular/compiler-cli": "^7.2.7",
- "@angular/language-service": "^7.2.7",
+ "@angular-devkit/build-angular": "^0.13.8",
+ "@angular/cli": "~7.3.8",
+ "@angular/compiler-cli": "^7.2.13",
+ "@angular/language-service": "^7.2.13",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
- "karma": "~3.1.1",
+ "karma": "^4.0.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
+ "sonar-scanner": "^3.1.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.2.2"
diff --git a/sonar-project.properties b/sonar-project.properties
new file mode 100644
index 0000000000000000000000000000000000000000..4661a59c4032c9ede3725e028d0575c5d164621b
--- /dev/null
+++ b/sonar-project.properties
@@ -0,0 +1,10 @@
+sonar.projectKey=xtss-catalogue
+sonar.sources=src
+sonar.sourceEncoding=UTF-8
+sonar.exclusions=**/node_modules/**,**/*.spec.ts,src/environments/**,src/karma.conf.js,src/main.ts
+sonar.tests=src
+sonar.test.inclusions=**/*.spec.ts
+sonar.typescript.lcov.reportPaths=coverage/xtss-catalogue/lcov.info
+sonar.host.url=http://localhost:9000
+#sonar.host.url=https://
+#sonar.login=
diff --git a/src/app/app-routing.module.spec.ts b/src/app/app-routing.module.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3f287aa856b76e3d23f58497430b224315f8f2c6
--- /dev/null
+++ b/src/app/app-routing.module.spec.ts
@@ -0,0 +1,13 @@
+import { AppRoutingModule } from './app-routing.module';
+
+describe('AppRoutingModule', () => {
+ let module: AppRoutingModule;
+
+ beforeEach(() => {
+ module = new AppRoutingModule();
+ });
+
+ it('should be created', () => {
+ expect(module).toBeTruthy();
+ });
+});
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 169a2dcc63e9532d1070bb158d00acaef63fe666..354d9273cde60fc900f1b9c4b2349c969f41d159 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -15,4 +15,4 @@ const routes: Routes = [
imports: [ RouterModule.forRoot(routes, {scrollPositionRestoration: 'enabled'}) ],
exports: [ RouterModule ]
})
-export class AppRoutingModule {}
\ No newline at end of file
+export class AppRoutingModule {}
diff --git a/src/app/app.component.css b/src/app/app.component.css
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 742bd73a7a45484d273a74cf395b0a548752b02f..589078ee142d41d7c4385735ed76e8fc11d844c7 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -2,4 +2,4 @@
-
\ No newline at end of file
+
diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts
index a4fd87655d25dc65ee96b128b7ce068653ce8379..c50815f65c22916a61a520d09e79ea0b413031e0 100644
--- a/src/app/app.component.spec.ts
+++ b/src/app/app.component.spec.ts
@@ -1,5 +1,5 @@
import { TestBed, async } from '@angular/core/testing';
-import {RouterTestingModule} from '@angular/router/testing'
+import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 68478a86238a14c44445df8301d49bce462ed788..ead0de4491ac51c7a45cbf97c9f487cc61acc19e 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -2,8 +2,7 @@ import { Component } from '@angular/core';
@Component({
selector: 'app-root',
- templateUrl: './app.component.html',
- styleUrls: ['./app.component.css']
+ templateUrl: './app.component.html'
})
export class AppComponent {
}
diff --git a/src/app/app.config-mock.ts b/src/app/app.config-mock.ts
new file mode 100644
index 0000000000000000000000000000000000000000..208efe5db5f5087337826d597d1fff1c12f01b9d
--- /dev/null
+++ b/src/app/app.config-mock.ts
@@ -0,0 +1,34 @@
+import { Injectable } from '@angular/core';
+import { AppConfig } from './app.config';
+
+@Injectable()
+export class AppConfigMock extends AppConfig {
+ private configMock: any = {
+ MAX_LIMIT: 1000000,
+ DEFAULT_LIMIT: 10,
+ LIMITS: {
+ 10: 10,
+ 20: 20,
+ 50: 50
+ },
+ INSTANCES: {
+ EE: 'https://www.x-tee.ee/catalogue/EE/wsdls/',
+ 'ee-test': 'https://www.x-tee.ee/catalogue/ee-test/wsdls/',
+ 'ee-dev': 'https://www.x-tee.ee/catalogue/ee-dev/wsdls/'
+ },
+ API_SERVICE: 'index.json',
+ API_HISTORY: 'history.json',
+ HISTORY_LIMIT: 30,
+ LANGUAGES: {
+ EST: 'est',
+ ENG: 'eng'
+ },
+ PREVIEW_SIZE: 5,
+ // Smaller value for faster unit testing
+ FILTER_DEBOUNCE: 20
+ };
+
+ public getConfig(key: any) {
+ return this.configMock[key];
+ }
+}
diff --git a/src/app/app.config.spec.ts b/src/app/app.config.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..20c6ddaa8e8dde0396b648345b44dae0a9315d6c
--- /dev/null
+++ b/src/app/app.config.spec.ts
@@ -0,0 +1,28 @@
+import { AppConfig } from './app.config';
+import { of } from 'rxjs';
+
+describe('AppConfig', () => {
+ let config: AppConfig;
+ let httpClientSpy: { get: jasmine.Spy };
+
+ beforeEach(() => {
+ httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
+ config = new AppConfig(httpClientSpy as any);
+ });
+
+ it('should be created', () => {
+ expect(config).toBeTruthy();
+ });
+
+ it('should load configuration', async () => {
+ httpClientSpy.get.and.returnValue(of({TEST: 'OK'}));
+ await config.load();
+ expect(httpClientSpy.get).toHaveBeenCalledWith('./assets/config.json');
+ });
+
+ it('getConfig should work', async () => {
+ httpClientSpy.get.and.returnValue(of({TEST: 'OK'}));
+ await config.load();
+ expect(config.getConfig('TEST')).toBe('OK');
+ });
+});
diff --git a/src/app/app.config.ts b/src/app/app.config.ts
new file mode 100644
index 0000000000000000000000000000000000000000..239927358d607226f186e4105387b79f6609f0e5
--- /dev/null
+++ b/src/app/app.config.ts
@@ -0,0 +1,31 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+@Injectable()
+export class AppConfig {
+ private config: any = null;
+
+ constructor(private http: HttpClient) { }
+
+ /**
+ * Use to get the data found in the config file
+ */
+ public getConfig(key: any) {
+ return this.config[key];
+ }
+
+ /**
+ * This method loads "config.json" to get all configuration variables
+ */
+ public load() {
+ return new Promise(resolve => {
+ // Not handling errors. App cannot work without valid configuration
+ this.http.get('./assets/config.json')
+ .subscribe(responseData => {
+ this.config = responseData;
+ resolve(true);
+ });
+
+ });
+ }
+}
diff --git a/src/app/app.module.spec.ts b/src/app/app.module.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9cc7eb094a6192f28059a896ded5b40bb1788761
--- /dev/null
+++ b/src/app/app.module.spec.ts
@@ -0,0 +1,35 @@
+import { AppModule, HttpLoaderFactory } from './app.module';
+import { TranslateHttpLoader } from '@ngx-translate/http-loader';
+import { AppConfig } from './app.config';
+import { TestBed } from '@angular/core/testing';
+
+describe('AppModule', () => {
+ let appModule: AppModule;
+ let httpClientSpy: { get: jasmine.Spy };
+ let appConfigSpy: { load: jasmine.Spy };
+
+ beforeEach(() => {
+ httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
+ appConfigSpy = jasmine.createSpyObj('HttpClient', ['load']);
+ appModule = new AppModule();
+ });
+
+ it('should be created', () => {
+ expect(appModule).toBeTruthy();
+ });
+
+ it('HttpLoaderFactory should work', () => {
+ expect(HttpLoaderFactory(httpClientSpy as any) instanceof TranslateHttpLoader).toBeTruthy();
+ });
+
+ it('AppConfig should be initialized', async () => {
+ TestBed.configureTestingModule({
+ imports: [ AppModule ],
+ providers: [
+ { provide: AppConfig, useValue: appConfigSpy }
+ ]
+ });
+ expect(TestBed.get(AppConfig)).toBeTruthy();
+ expect(appConfigSpy.load).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index d6f95a708846ae5c479fc3350660af41ea89a9a2..6474b7c7456acd438af100344aed624e6a0af805 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -11,6 +11,9 @@ import { SubsystemItemComponent } from './subsystem-list/subsystem-item/subsyste
import { AppRoutingModule } from './app-routing.module';
import { SubsystemComponent } from './subsystem/subsystem.component';
import { HeaderComponent } from './header/header.component';
+import { APP_INITIALIZER } from '@angular/core';
+import { AppConfig } from './app.config';
+import { MessagesComponent } from './messages/messages.component';
@NgModule({
declarations: [
@@ -19,7 +22,8 @@ import { HeaderComponent } from './header/header.component';
SearchComponent,
SubsystemItemComponent,
SubsystemComponent,
- HeaderComponent
+ HeaderComponent,
+ MessagesComponent
],
imports: [
BrowserModule,
@@ -34,7 +38,10 @@ import { HeaderComponent } from './header/header.component';
}
})
],
- providers: [],
+ providers: [
+ AppConfig,
+ { provide: APP_INITIALIZER, useFactory: (config: AppConfig) => () => config.load(), deps: [AppConfig], multi: true }
+ ],
bootstrap: [AppComponent]
})
export class AppModule { }
@@ -42,4 +49,4 @@ export class AppModule { }
export function HttpLoaderFactory(http: HttpClient) {
// Providing path as a workaround for ngx-translate bug with --base-href option
return new TranslateHttpLoader(http, './assets/i18n/');
-}
\ No newline at end of file
+}
diff --git a/src/app/header/header.component.css b/src/app/header/header.component.css
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html
index 68c6d1db28f7b5cf63dbbe44ff3f7077168facd0..aee64ac5438939491ad6ab87fc3cdfef2e924fcd 100644
--- a/src/app/header/header.component.html
+++ b/src/app/header/header.component.html
@@ -7,4 +7,4 @@
-
\ No newline at end of file
+
diff --git a/src/app/header/header.component.spec.ts b/src/app/header/header.component.spec.ts
index e378666f14a64e4ce399869ab1cf0e90e63691d9..d09d6647a2b40c1ab0665e7ec63392168d80246e 100644
--- a/src/app/header/header.component.spec.ts
+++ b/src/app/header/header.component.spec.ts
@@ -2,6 +2,9 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { HeaderComponent } from './header.component';
import { HttpClientModule } from '@angular/common/http';
+import { LanguagesService } from '../languages.service';
+import { AppConfigMock } from 'src/app/app.config-mock';
+import { AppConfig } from 'src/app/app.config';
describe('HeaderComponent', () => {
let component: HeaderComponent;
@@ -13,6 +16,9 @@ describe('HeaderComponent', () => {
imports: [
TranslateModule.forRoot(),
HttpClientModule
+ ],
+ providers: [
+ { provide: AppConfig, useClass: AppConfigMock }
]
})
.compileComponents();
@@ -27,4 +33,11 @@ describe('HeaderComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ it('should set language', () => {
+ const languagesService: LanguagesService = TestBed.get(LanguagesService);
+ spyOn(languagesService, 'setLang').and.returnValue(null);
+ component.setLang('xxx');
+ expect(languagesService.setLang).toHaveBeenCalledWith('xxx');
+ });
});
diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts
index 20b3337f265623066468c1ca3f641a0faebeccd5..75354d018dea1a9eb17dcff181432d15382081c5 100644
--- a/src/app/header/header.component.ts
+++ b/src/app/header/header.component.ts
@@ -3,25 +3,23 @@ import { LanguagesService } from '../languages.service';
@Component({
selector: 'app-header',
- templateUrl: './header.component.html',
- styleUrls: ['./header.component.css']
+ templateUrl: './header.component.html'
})
export class HeaderComponent implements OnInit {
constructor(private languagesService: LanguagesService) { }
- ngOnInit() {
+ getLangs(): string[] {
+ return this.languagesService.getLangs();
}
- getLangs():string[] {
- return this.languagesService.getLangs()
- }
-
- getLang():string {
- return this.languagesService.getLang()
+ getLang(): string {
+ return this.languagesService.getLang();
}
setLang(lang: string) {
- return this.languagesService.setLang(lang)
+ return this.languagesService.setLang(lang);
}
+
+ ngOnInit() {}
}
diff --git a/src/app/instance-version.ts b/src/app/instance-version.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f69b6f1d6d9ddbf3224165fce89995e3f58daa28
--- /dev/null
+++ b/src/app/instance-version.ts
@@ -0,0 +1,5 @@
+export class InstanceVersion {
+ reportTime: string;
+ reportTimeCompact: string;
+ reportPath: string;
+}
diff --git a/src/app/languages.service.spec.ts b/src/app/languages.service.spec.ts
index 98d5256a4f8b84c086edca52378d18ae43dd3185..d857a5e93411b3b64ade0f222fba38329693cae5 100644
--- a/src/app/languages.service.spec.ts
+++ b/src/app/languages.service.spec.ts
@@ -1,11 +1,18 @@
import { TestBed } from '@angular/core/testing';
-import { TranslateModule } from '@ngx-translate/core';
import { LanguagesService } from './languages.service';
+import { TranslateModule, TranslateService } from '@ngx-translate/core';
+import { AppConfigMock } from 'src/app/app.config-mock';
+import { AppConfig } from 'src/app/app.config';
+import { HttpClientModule } from '@angular/common/http';
describe('LanguagesService', () => {
beforeEach(() => TestBed.configureTestingModule({
imports: [
- TranslateModule.forRoot()
+ TranslateModule.forRoot(),
+ HttpClientModule
+ ],
+ providers: [
+ { provide: AppConfig, useClass: AppConfigMock }
]
}));
@@ -13,4 +20,28 @@ describe('LanguagesService', () => {
const service: LanguagesService = TestBed.get(LanguagesService);
expect(service).toBeTruthy();
});
+
+ it('should set default lang with empty localStorage', () => {
+ const translateService: TranslateService = TestBed.get(TranslateService);
+ spyOn(translateService, 'setDefaultLang');
+ spyOn(window.localStorage, 'getItem').and.returnValue(undefined);
+ TestBed.get(LanguagesService);
+ expect(translateService.setDefaultLang).toHaveBeenCalledWith('est');
+ });
+
+ it('should set default lang from localStorage', () => {
+ const translateService: TranslateService = TestBed.get(TranslateService);
+ spyOn(translateService, 'setDefaultLang');
+ spyOn(window.localStorage, 'getItem').and.returnValue('ENG');
+ TestBed.get(LanguagesService);
+ expect(translateService.setDefaultLang).toHaveBeenCalledWith('eng');
+ });
+
+ it('should set language', () => {
+ const translateService: TranslateService = TestBed.get(TranslateService);
+ spyOn(translateService, 'use');
+ const service = TestBed.get(LanguagesService);
+ service.setLang('ENG');
+ expect(translateService.use).toHaveBeenCalledWith('eng');
+ });
});
diff --git a/src/app/languages.service.ts b/src/app/languages.service.ts
index bcf9b0dd6049b7e6b3177d024750f34ea15ea8a3..8f13fd50353ba3333e6411a6a1baafa20a8c950b 100644
--- a/src/app/languages.service.ts
+++ b/src/app/languages.service.ts
@@ -1,60 +1,57 @@
import { Injectable } from '@angular/core';
-import {TranslateService} from "@ngx-translate/core";
+import {TranslateService} from '@ngx-translate/core';
import { Title } from '@angular/platform-browser';
import { Subscription } from 'rxjs';
import { take } from 'rxjs/operators';
-
-const LANGUAGES = {
- 'EST': 'est',
- 'ENG': 'eng'
-}
+import { AppConfig } from './app.config';
@Injectable({
providedIn: 'root'
})
export class LanguagesService {
- private selectedLang = ''
- private translateSubscription: Subscription
-
+ private selectedLang = '';
+ private translateSubscription: Subscription;
+
constructor(
private translate: TranslateService,
- private title: Title
+ private title: Title,
+ private config: AppConfig
) {
- this.selectedLang = this.getDefaultLang()
- translate.setDefaultLang(LANGUAGES[this.selectedLang])
- this.updateTitle()
+ this.selectedLang = this.getDefaultLang();
+ translate.setDefaultLang(this.config.getConfig('LANGUAGES')[this.selectedLang]);
+ this.updateTitle();
}
updateTitle(): void {
// Not subscribing if subscription already in progress
if (!this.translateSubscription || this.translateSubscription.closed) {
this.translateSubscription = this.translate.get('index.title').pipe(take(1)).subscribe((res: string) => {
- this.title.setTitle(res)
+ this.title.setTitle(res);
});
}
}
getDefaultLang(): string {
- if(window && window.localStorage && window.localStorage.getItem('lang')) {
- return window.localStorage.getItem('lang')
+ if (window && window.localStorage && window.localStorage.getItem('lang')) {
+ return window.localStorage.getItem('lang');
}
- return Object.keys(LANGUAGES)[0]
+ return Object.keys(this.config.getConfig('LANGUAGES'))[0];
}
- getLang(): string {
- return this.selectedLang
+ getLangs(): string[] {
+ return Object.keys(this.config.getConfig('LANGUAGES'));
}
- getLangs(): string[] {
- return Object.keys(LANGUAGES)
+ getLang(): string {
+ return this.selectedLang;
}
setLang(lang: string): void {
- this.selectedLang = lang
- if(window && window.localStorage) {
- window.localStorage.setItem('lang', this.selectedLang)
+ this.selectedLang = lang;
+ if (window && window.localStorage) {
+ window.localStorage.setItem('lang', this.selectedLang);
}
- this.translate.use(LANGUAGES[this.selectedLang]);
- this.updateTitle()
+ this.translate.use(this.config.getConfig('LANGUAGES')[this.selectedLang]);
+ this.updateTitle();
}
}
diff --git a/src/app/messages/messages.component.html b/src/app/messages/messages.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..ef9536e8a3fc16126eb7944470c1720e74d6f208
--- /dev/null
+++ b/src/app/messages/messages.component.html
@@ -0,0 +1,3 @@
+
+ {{message | translate:{"subsystem": subsystemId} }}
+
diff --git a/src/app/messages/messages.component.spec.ts b/src/app/messages/messages.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3f4fb72998f6e17ff8ae6f92e6f6e8a3675bafa0
--- /dev/null
+++ b/src/app/messages/messages.component.spec.ts
@@ -0,0 +1,32 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { TranslateModule } from '@ngx-translate/core';
+import { HttpClientModule } from '@angular/common/http';
+import { MessagesComponent } from './messages.component';
+
+describe('MessagesComponent', () => {
+ let component: MessagesComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ MessagesComponent
+ ],
+ imports: [
+ TranslateModule.forRoot(),
+ HttpClientModule
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MessagesComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/messages/messages.component.ts b/src/app/messages/messages.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b065dc539a09a9b1303747967947435c34a43ff7
--- /dev/null
+++ b/src/app/messages/messages.component.ts
@@ -0,0 +1,16 @@
+import { Component, OnInit, Input } from '@angular/core';
+
+@Component({
+ selector: 'app-messages',
+ templateUrl: './messages.component.html'
+})
+export class MessagesComponent implements OnInit {
+ @Input() message: '';
+ @Input() subsystemId: '';
+
+ constructor() { }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/src/app/methods.service.spec.ts b/src/app/methods.service.spec.ts
deleted file mode 100644
index eb25906193a178a10f11fae01a9396b4dedb09e2..0000000000000000000000000000000000000000
--- a/src/app/methods.service.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
-import { MethodsService } from './methods.service';
-import { HttpClient, HttpClientModule } from '@angular/common/http';
-import { HttpLoaderFactory } from './app.module';
-
-describe('MethodsService', () => {
- beforeEach(() => TestBed.configureTestingModule({
- imports: [
- TranslateModule.forRoot({
- loader: {
- provide: TranslateLoader,
- useFactory: HttpLoaderFactory,
- deps: [HttpClient]
- }
- }),
- HttpClientModule
- ]
- }));
-
- it('should be created', () => {
- const service: MethodsService = TestBed.get(MethodsService);
- expect(service).toBeTruthy();
- });
-});
\ No newline at end of file
diff --git a/src/app/methods.service.ts b/src/app/methods.service.ts
deleted file mode 100644
index da62d947b37551c2b3bc762ff90c16fae355e914..0000000000000000000000000000000000000000
--- a/src/app/methods.service.ts
+++ /dev/null
@@ -1,222 +0,0 @@
-import { Injectable, Output, EventEmitter } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { Observable, of } from 'rxjs';
-import { catchError } from 'rxjs/operators';
-import { Subsystem } from './subsystem';
-import { Method } from './method';
-
-const MAX_LIMIT: number = 1000000;
-const CONFIG = {
- 'EE': 'https://www.x-tee.ee/catalogue/EE/wsdls/',
- 'ee-test': 'https://www.x-tee.ee/catalogue/ee-test/wsdls/',
- 'ee-dev': 'https://www.x-tee.ee/catalogue/ee-dev/wsdls/'
-}
-const API_SERVICE = 'index.json';
-
-@Injectable({
- providedIn: 'root'
-})
-export class MethodsService {
- private apiUrlBase = '';
- private limit: number = 10;
- private offset: number = 0;
- private nonEmpty: boolean = false;
- private filter: string = "";
- private subsystems: Subsystem[] = [];
- private loadingDone: boolean = false;
- private loadingError: boolean = false;
- private instance: string = '';
- private instanceData = new Object();
-
- @Output() subsystemsUpdated: EventEmitter = new EventEmitter();
- @Output() warnings: EventEmitter = new EventEmitter();
-
- constructor(private http: HttpClient) {}
-
- private signalRefresh() {
- this.subsystemsUpdated.emit(null);
- }
-
- private filteredSubsystems(): Subsystem[] {
- let filtered: Subsystem[] = []
- let limit: number = this.limit
- for (let subsystem of this.subsystems) {
- if (this.nonEmpty && !subsystem.methods.length) {
- // Filtering out empty subsystems
- continue
- }
- if (
- this.filter != ''
- && !subsystem.methods.length
- ) {
- // Subsystem without methods
- if (subsystem.fullSubsystemName.toLowerCase().indexOf(this.filter.toLowerCase()) < 0) {
- // Subsystem name does not match the filter
- continue
- }
- } else if (this.filter != '') {
- // Subsystem with methods
- let filteredMethods: Method[] = []
- for (let method of subsystem.methods) {
- if(method.fullMethodName.toLowerCase().indexOf(this.filter.toLowerCase()) >= 0) {
- filteredMethods.push(method)
- }
- }
- if (!filteredMethods.length) {
- // No matching method names found
- continue
- }
-
- // Copy object to avoid overwriting methods array in subsystem object
- // TODO: Is there a better way???
- subsystem = Object.assign(Object.create(subsystem), subsystem);
- // Leaving only matcing methods
- subsystem.methods = filteredMethods
- }
-
- filtered.push(subsystem)
- limit -= 1
- if (limit == 0) {
- break
- }
- }
-
- return filtered.slice(this.offset, this.limit);
- }
-
- // TODO: move that to Class constructor?
- private setFullNames() {
- for (let i in this.subsystems) {
- this.subsystems[i].fullSubsystemName = this.subsystems[i].xRoadInstance
- + '/' + this.subsystems[i].memberClass
- + '/' + this.subsystems[i].memberCode
- + '/' + this.subsystems[i].subsystemCode
- for (let j in this.subsystems[i].methods) {
- this.subsystems[i].methods[j].fullMethodName = this.subsystems[i].fullSubsystemName
- + '/' + this.subsystems[i].methods[j].serviceCode
- + '/' + this.subsystems[i].methods[j].serviceVersion
- }
- }
- }
-
- /**
- * Handle Http operation that failed.
- * Let the app continue.
- * @param result - optional value to return as the observable result
- */
- private handleError (result?: T) {
- return (error: any): Observable => {
- this.loadingError = true
- this.emitWarning('Error while loading data from server!')
-
- // Let the app keep running by returning an empty result.
- return of(result as T);
- };
- }
-
- getDefaultInstance(): string {
- return Object.keys(CONFIG)[0]
- }
-
- getInstances(): string[] {
- return Object.keys(CONFIG)
- }
-
- getInstance(): string {
- return this.instance
- }
-
- emitWarning(msg: string) {
- this.warnings.emit(msg);
- }
-
- setInstance(instance: string) {
- this.instance = instance
- this.apiUrlBase = CONFIG[instance]
-
- // Data of this instance already loaded
- if (this.instanceData[instance] && this.instanceData[instance].length) {
- this.subsystems = this.instanceData[instance]
- this.signalRefresh();
- } else {
- this.loadingDone = false;
- this.loadingError = false;
- this.http.get(this.apiUrlBase + API_SERVICE)
- .pipe(
- catchError(this.handleError([]))
- ).subscribe(subsystems => {
- this.instanceData[instance] = subsystems
- this.subsystems = this.instanceData[instance]
- this.setFullNames();
- this.loadingDone = true;
- this.signalRefresh();
- })
- }
- }
-
- getApiUrlBase(): string {
- return this.apiUrlBase
- }
-
- getLimit(): string {
- if (this.limit == MAX_LIMIT) {
- return 'all'
- }
- return this.limit.toString()
- }
-
- getNonEmpty(): boolean {
- return this.nonEmpty
- }
-
- getfilter(): string {
- return this.filter
- }
-
- isLoadingDone(): boolean {
- return this.loadingDone
- }
-
- isLoadingError(): boolean {
- return this.loadingError
- }
-
- getMethods(): Subsystem[] {
- return this.filteredSubsystems();
- }
-
- setNonEmpty(nonEmpty: boolean) {
- this.nonEmpty = nonEmpty;
- this.signalRefresh();
- }
-
- setLimit (limit: string) {
- switch(limit) {
- case '20':
- this.limit = 20;
- break;
- case '50':
- this.limit = 50;
- break;
- case 'all':
- this.limit = MAX_LIMIT;
- break;
- default:
- this.limit = 10;
- }
- this.signalRefresh();
- }
-
- setFilter(filter: string) {
- if (this.filter != filter.trim()) {
- this.filter = filter.trim();
- this.signalRefresh();
- }
- }
-
- getSubsystem(name: string): Subsystem {
- return this.subsystems.find(function(element) {
- return element.fullSubsystemName === name;
- })
- }
-}
diff --git a/src/app/subsystem-list/search/search.component.css b/src/app/subsystem-list/search/search.component.css
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/app/subsystem-list/search/search.component.html b/src/app/subsystem-list/search/search.component.html
index d702833ccd5b96f654294e08ad00c71e95d515aa..9f8615bd1311a5a2a90dc3864803cf326b35772d 100644
--- a/src/app/subsystem-list/search/search.component.html
+++ b/src/app/subsystem-list/search/search.component.html
@@ -16,9 +16,7 @@
@@ -32,4 +30,4 @@
-
\ No newline at end of file
+
diff --git a/src/app/subsystem-list/search/search.component.spec.ts b/src/app/subsystem-list/search/search.component.spec.ts
index 583b17d4bfd999de833f67926248d9d98941d6dc..c5e08d0fe7b6fd357820552471d57491f5d9b8a1 100644
--- a/src/app/subsystem-list/search/search.component.spec.ts
+++ b/src/app/subsystem-list/search/search.component.spec.ts
@@ -3,10 +3,14 @@ import { TranslateModule } from '@ngx-translate/core';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { SearchComponent } from './search.component';
+import { SubsystemsService } from 'src/app/subsystems.service';
+import { AppConfigMock } from 'src/app/app.config-mock';
+import { AppConfig } from 'src/app/app.config';
describe('SearchComponent', () => {
let component: SearchComponent;
let fixture: ComponentFixture;
+ let subsystemsService: SubsystemsService;
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -15,12 +19,20 @@ describe('SearchComponent', () => {
FormsModule,
TranslateModule.forRoot(),
HttpClientModule
+ ],
+ providers: [
+ { provide: AppConfig, useClass: AppConfigMock }
]
})
.compileComponents();
}));
beforeEach(() => {
+ subsystemsService = TestBed.get(SubsystemsService);
+ spyOn(subsystemsService, 'getLimits').and.returnValue({10: 10, 20: 20});
+ spyOn(subsystemsService, 'setNonEmpty').and.returnValue(null);
+ spyOn(subsystemsService, 'setLimit').and.returnValue(null);
+ spyOn(subsystemsService, 'setFilter').and.returnValue(null);
fixture = TestBed.createComponent(SearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
@@ -29,4 +41,24 @@ describe('SearchComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
-});
\ No newline at end of file
+
+ it('getLimitKeys should work', () => {
+ expect(component.getLimitKeys()).toEqual(['10', '20']);
+ expect(subsystemsService.getLimits).toHaveBeenCalledWith();
+ });
+
+ it('setNonEmpty should work', () => {
+ component.setNonEmpty(true);
+ expect(subsystemsService.setNonEmpty).toHaveBeenCalledWith(true);
+ });
+
+ it('setLimit should work', () => {
+ component.setLimit('50');
+ expect(subsystemsService.setLimit).toHaveBeenCalledWith('50');
+ });
+
+ it('setFilter should work', () => {
+ component.setFilter('test');
+ expect(subsystemsService.setFilter).toHaveBeenCalledWith('test');
+ });
+});
diff --git a/src/app/subsystem-list/search/search.component.ts b/src/app/subsystem-list/search/search.component.ts
index 5c48c55db1ac525af403187c0ba8b015fbd0b252..f98d8a3c2ff1f17f5f1e9ba56def4b8a5451d022 100644
--- a/src/app/subsystem-list/search/search.component.ts
+++ b/src/app/subsystem-list/search/search.component.ts
@@ -1,35 +1,40 @@
import { Component, OnInit } from '@angular/core';
-import { MethodsService } from '../../methods.service';
+import { SubsystemsService } from '../../subsystems.service';
@Component({
selector: 'app-search',
- templateUrl: './search.component.html',
- styleUrls: ['./search.component.css']
+ templateUrl: './search.component.html'
})
export class SearchComponent implements OnInit {
+ limit: string;
+ limits: object;
+ nonEmpty: boolean;
+ filter: string;
+
+ constructor(private subsystemsService: SubsystemsService) {
+ this.limit = this.subsystemsService.getLimit();
+ this.limits = this.subsystemsService.getLimits();
+ }
- limit: string
- nonEmpty: boolean
- filter: string
-
- constructor(private methodsService: MethodsService) { }
-
- ngOnInit() {
- this.limit = this.methodsService.getLimit()
- this.nonEmpty = this.methodsService.getNonEmpty()
- this.filter = this.methodsService.getfilter()
+ getLimitKeys(): string[] {
+ return Object.keys(this.limits);
}
setNonEmpty(nonEmpty: boolean) {
- this.methodsService.setNonEmpty(nonEmpty)
+ this.subsystemsService.setNonEmpty(nonEmpty);
}
setLimit(limit: string) {
- this.methodsService.setLimit(limit)
+ this.subsystemsService.setLimit(limit);
}
setFilter(filter: string) {
- this.methodsService.setFilter(filter)
+ this.subsystemsService.setFilter(filter);
}
+ ngOnInit() {
+ this.limit = this.subsystemsService.getLimit();
+ this.nonEmpty = this.subsystemsService.getNonEmpty();
+ this.filter = this.subsystemsService.getfilter();
+ }
}
diff --git a/src/app/subsystem-list/subsystem-item/subsystem-item.component.css b/src/app/subsystem-list/subsystem-item/subsystem-item.component.css
index f4be51e77a6c1fb61da9c424800e086845acd1f3..c2c466066310cb2fa8646fcd4e8e330abfaf05ff 100644
--- a/src/app/subsystem-list/subsystem-item/subsystem-item.component.css
+++ b/src/app/subsystem-list/subsystem-item/subsystem-item.component.css
@@ -1,3 +1,3 @@
.pointerCursor {
cursor: pointer;
-}
\ No newline at end of file
+}
diff --git a/src/app/subsystem-list/subsystem-item/subsystem-item.component.html b/src/app/subsystem-list/subsystem-item/subsystem-item.component.html
index 754aae29dcec702813c94ce09848829e3329c750..18f95dcc40c4ca866adc14654bfd032e1ed6c546 100644
--- a/src/app/subsystem-list/subsystem-item/subsystem-item.component.html
+++ b/src/app/subsystem-list/subsystem-item/subsystem-item.component.html
@@ -15,10 +15,12 @@
{{method.fullMethodName}}
WSDL
- {{'subsystem.statusWsdlError' | translate}}
-
+ {{'subsystem.statusWsdlError' | translate}}
+ {{'subsystem.statusWsdlTimeout' | translate}}
+ {{'subsystem.statusWsdlSkipped' | translate}}
+
0" class="pointerCursor" (click)="showDetail()">
{{'subsystem.moreMethods' | translate:{"count": getNotInPreview()} }}
-
\ No newline at end of file
+
diff --git a/src/app/subsystem-list/subsystem-item/subsystem-item.component.spec.ts b/src/app/subsystem-list/subsystem-item/subsystem-item.component.spec.ts
index b12284aae6bfe463c98a2fd75d5231436a487fa8..2a8304a46fb2ec6c279f56ed000ae5bb6b6c9ac7 100644
--- a/src/app/subsystem-list/subsystem-item/subsystem-item.component.spec.ts
+++ b/src/app/subsystem-list/subsystem-item/subsystem-item.component.spec.ts
@@ -3,43 +3,90 @@ import { TranslateModule } from '@ngx-translate/core';
import { HttpClientModule } from '@angular/common/http';
import { SubsystemItemComponent } from './subsystem-item.component';
import { RouterTestingModule } from '@angular/router/testing';
+import { SubsystemsService } from 'src/app/subsystems.service';
+import { Router } from '@angular/router';
+import { Method } from 'src/app/method';
+import { AppConfigMock } from 'src/app/app.config-mock';
+import { AppConfig } from 'src/app/app.config';
+
+const PREVIEW_SIZE = 5;
describe('SubsystemItemComponent', () => {
let component: SubsystemItemComponent;
let fixture: ComponentFixture;
+ let subsystemsService: SubsystemsService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
- SubsystemItemComponent
+ SubsystemItemComponent
],
imports: [
TranslateModule.forRoot(),
HttpClientModule,
RouterTestingModule
+ ],
+ providers: [
+ { provide: Router, useValue: {
+ navigateByUrl: jasmine.createSpy('navigateByUrl')
+ }},
+ { provide: AppConfig, useClass: AppConfigMock }
]
})
.compileComponents();
}));
beforeEach(() => {
+ subsystemsService = TestBed.get(SubsystemsService);
+ spyOn(subsystemsService, 'getApiUrlBase').and.returnValue(null);
+
fixture = TestBed.createComponent(SubsystemItemComponent);
component = fixture.componentInstance;
-
component.subsystem = {
- xRoadInstance: "XRD",
- memberClass: "CLASS",
- memberCode: "CODE",
- subsystemCode: "SUB",
- subsystemStatus: "OK",
- fullSubsystemName: "XRD/CLASS/CODE/SUB",
+ xRoadInstance: 'INST',
+ memberClass: 'CLASS',
+ memberCode: 'CODE',
+ subsystemCode: 'SUB',
+ subsystemStatus: 'OK',
+ fullSubsystemName: 'INST/CLASS/CODE/SUB',
methods: []
- }
-
+ };
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
-});
\ No newline at end of file
+
+ it('getApiUrlBase should work', () => {
+ component.getApiUrlBase();
+ expect(subsystemsService.getApiUrlBase).toHaveBeenCalled();
+ });
+
+ it('should preview methods', () => {
+ expect(component.getMethodsPreview().length).toBe(0);
+ for (let i = 0; i < PREVIEW_SIZE + 10; i++) {
+ component.subsystem.methods.push(new Method());
+ }
+ expect(component.getMethodsPreview().length).toBe(PREVIEW_SIZE);
+ });
+
+ it('should calculate methods not in preview', () => {
+ expect(component.getNotInPreview()).toBe(0);
+ for (let i = 0; i < PREVIEW_SIZE + 10; i++) {
+ component.subsystem.methods.push(new Method());
+ }
+ expect(component.getNotInPreview()).toBe(10);
+ });
+
+ it('should go to detail view', () => {
+ const spy = TestBed.get(Router).navigateByUrl;
+ component.showDetail();
+ expect(spy).toHaveBeenCalledWith('/INST/CLASS/CODE/SUB');
+
+ spy.calls.reset();
+ spyOn(subsystemsService, 'getInstanceVersion').and.returnValue('12345');
+ component.showDetail();
+ expect(spy).toHaveBeenCalledWith('/INST/CLASS/CODE/SUB?at=12345');
+ });
+});
diff --git a/src/app/subsystem-list/subsystem-item/subsystem-item.component.ts b/src/app/subsystem-list/subsystem-item/subsystem-item.component.ts
index 9b9aeea838aaf1f8e4c98552b942835c77bb5de2..4dace562a56880d4680d063b89b3d9730e25a525 100644
--- a/src/app/subsystem-list/subsystem-item/subsystem-item.component.ts
+++ b/src/app/subsystem-list/subsystem-item/subsystem-item.component.ts
@@ -1,8 +1,9 @@
import { Component, OnInit, Input } from '@angular/core';
import { Subsystem } from '../../subsystem';
import { Method } from '../../method';
-import { MethodsService } from '../../methods.service';
+import { SubsystemsService } from '../../subsystems.service';
import { Router } from '@angular/router';
+import { AppConfig } from '../../app.config';
@Component({
selector: 'app-subsystem-item',
@@ -10,31 +11,27 @@ import { Router } from '@angular/router';
styleUrls: ['./subsystem-item.component.css']
})
export class SubsystemItemComponent implements OnInit {
-
- @Input() subsystem: Subsystem
- private previewSize: number = 5
+ @Input() subsystem: Subsystem;
constructor(
- private methodsService: MethodsService,
- private router: Router
+ private subsystemsService: SubsystemsService,
+ private router: Router,
+ private config: AppConfig
) { }
- ngOnInit() {
- }
-
getApiUrlBase(): string {
- return this.methodsService.getApiUrlBase()
+ return this.subsystemsService.getApiUrlBase();
}
getMethodsPreview(): Method[] {
- return this.subsystem.methods.length ? this.subsystem.methods.slice(0, this.previewSize) : []
+ return this.subsystem.methods.length ? this.subsystem.methods.slice(0, this.config.getConfig('PREVIEW_SIZE')) : [];
}
getNotInPreview(): number {
- if (this.subsystem.methods.length - this.previewSize < 0) {
- return 0
+ if (this.subsystem.methods.length - this.config.getConfig('PREVIEW_SIZE') < 0) {
+ return 0;
}
- return this.subsystem.methods.length - this.previewSize
+ return this.subsystem.methods.length - this.config.getConfig('PREVIEW_SIZE');
}
showDetail() {
@@ -43,6 +40,9 @@ export class SubsystemItemComponent implements OnInit {
+ '/' + this.subsystem.memberClass
+ '/' + this.subsystem.memberCode
+ '/' + this.subsystem.subsystemCode
- )
+ + (this.subsystemsService.getInstanceVersion() ? '?at=' + this.subsystemsService.getInstanceVersion() : '')
+ );
}
+
+ ngOnInit() {}
}
diff --git a/src/app/subsystem-list/subsystem-list.component.css b/src/app/subsystem-list/subsystem-list.component.css
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/app/subsystem-list/subsystem-list.component.html b/src/app/subsystem-list/subsystem-list.component.html
index c406243caf925aa0effc69cec58185a7c78a61cf..ff0f642e109f2f730ffc6ba08ccac41153cd989f 100644
--- a/src/app/subsystem-list/subsystem-list.component.html
+++ b/src/app/subsystem-list/subsystem-list.component.html
@@ -4,11 +4,10 @@
-
+
+
-
- {{message}}
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+{{'subsystemList.moreSubsystems' | translate}}
+
+
+
+
diff --git a/src/app/subsystem-list/subsystem-list.component.spec.ts b/src/app/subsystem-list/subsystem-list.component.spec.ts
index 309a200f435924bca31e9d254fe9c362e978dc4a..1e97f57c0475296656b985a762abdad8472649ce 100644
--- a/src/app/subsystem-list/subsystem-list.component.spec.ts
+++ b/src/app/subsystem-list/subsystem-list.component.spec.ts
@@ -1,55 +1,249 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { HttpClientModule } from '@angular/common/http';
-import { RouterTestingModule } from '@angular/router/testing'
import { SubsystemListComponent } from './subsystem-list.component';
-import { Component, Input, Output, EventEmitter } from '@angular/core';
-import { ViewportScroller } from '@angular/common';
+import { Component, Input } from '@angular/core';
import { Subsystem } from '../subsystem';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Router, Scroll } from '@angular/router';
import { of } from 'rxjs';
-
+import { SubsystemsService } from '../subsystems.service';
+import { ViewportScroller } from '@angular/common';
+import { AppConfigMock } from 'src/app/app.config-mock';
+import { AppConfig } from 'src/app/app.config';
+import { FormsModule } from '@angular/forms';
+
@Component({selector: 'app-header', template: ''})
class HeaderStubComponent {}
+@Component({selector: 'app-messages', template: ''})
+class MessagesStubComponent {
+ @Input() message: string;
+}
@Component({selector: 'app-search', template: ''})
class SearchStubComponent {}
@Component({selector: 'app-subsystem-item', template: ''})
class SubsystemItemStubComponent {
- @Input() subsystem: Subsystem
+ @Input() subsystem: Subsystem;
}
describe('SubsystemListComponent', () => {
let component: SubsystemListComponent;
let fixture: ComponentFixture;
+ let getInstanceSpy;
+ let getInstancesSpy;
+ let subsystemsService: SubsystemsService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
SubsystemListComponent,
HeaderStubComponent,
+ MessagesStubComponent,
SearchStubComponent,
SubsystemItemStubComponent
],
imports: [
+ FormsModule,
TranslateModule.forRoot(),
- HttpClientModule,
- RouterTestingModule
+ HttpClientModule
+ ],
+ providers: [
+ { provide: ActivatedRoute, useValue: {
+ params: of({
+ instance: 'INST'
+ })
+ }},
+ { provide: Router, useValue: {
+ events: of(new Scroll(null, [11, 12], null)),
+ navigateByUrl: jasmine.createSpy('navigateByUrl')
+ }},
+ { provide: AppConfig, useClass: AppConfigMock }
]
})
.compileComponents();
}));
beforeEach(() => {
- // Mocks and spies
- TestBed.get(ActivatedRoute).params = of({
- "instance": "EE"
- })
+ subsystemsService = TestBed.get(SubsystemsService);
+ getInstanceSpy = spyOn(subsystemsService, 'getInstance').and.returnValue('INST');
+ getInstancesSpy = spyOn(subsystemsService, 'getInstances').and.returnValue(['INST']);
+ spyOn(TestBed.get(ViewportScroller), 'scrollToPosition');
+ spyOn(subsystemsService, 'setInstance').and.returnValue(null);
+ spyOn(subsystemsService, 'getDefaultInstance').and.returnValue('DEFINST');
+ spyOn(TestBed.get(SubsystemsService), 'getApiUrlBase').and.returnValue('base');
+ });
+
+ it('should create', () => {
+ fixture = TestBed.createComponent(SubsystemListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ expect(component).toBeTruthy();
+ });
+
+ it('should redirect on incorrect instance', () => {
+ getInstancesSpy.and.returnValue(['XXX']);
+ fixture = TestBed.createComponent(SubsystemListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ expect(TestBed.get(Router).navigateByUrl).toHaveBeenCalledWith('/DEFINST');
+ });
+
+ it('should detect when instance is not selected', () => {
+ getInstanceSpy.and.returnValue('');
+ fixture = TestBed.createComponent(SubsystemListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ expect(subsystemsService.setInstance).toHaveBeenCalledWith('INST', '');
+ });
+
+ it('should detect change instance', () => {
+ getInstanceSpy.and.returnValue('INST2');
+ getInstancesSpy.and.returnValue(['INST', 'INST2']);
+ fixture = TestBed.createComponent(SubsystemListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ expect(subsystemsService.setInstance).toHaveBeenCalledWith('INST', '');
+ });
+
+ it('should scroll to position', () => {
+ fixture = TestBed.createComponent(SubsystemListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ expect(TestBed.get(ViewportScroller).scrollToPosition).toHaveBeenCalledWith([11, 12]);
+ });
+
+ it('switchInstance should work', () => {
+ fixture = TestBed.createComponent(SubsystemListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ const spy = TestBed.get(Router).navigateByUrl;
+
+ component.switchInstance('NEWINST');
+ expect(spy).toHaveBeenCalledWith('/NEWINST');
+
+ spy.calls.reset();
+ component.switchInstance('INST');
+ expect(spy).toHaveBeenCalledWith('/INST');
+ });
+
+ it('should receive service warnings', () => {
+ fixture = TestBed.createComponent(SubsystemListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ subsystemsService.warnings.emit('WARN');
+ expect(component.message).toBe('WARN');
+ });
+
+ it('scrollToTop should work', () => {
+ fixture = TestBed.createComponent(SubsystemListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ const spy = TestBed.get(ViewportScroller).scrollToPosition;
+ spy.calls.reset();
+ component.scrollToTop();
+ expect(spy).toHaveBeenCalledWith([0, 0]);
+ });
+
+ it('isPartialList should work', () => {
+ fixture = TestBed.createComponent(SubsystemListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+
+ subsystemsService.filteredSubsystemsSubject.next([new Subsystem(), new Subsystem()]);
+
+ const getLimitSpy = spyOn(subsystemsService, 'getLimit').and.returnValue(['all']);
+ expect(component.isPartialList()).toBeFalsy();
+
+ getLimitSpy.and.returnValue(['2']);
+ expect(component.isPartialList()).toBeTruthy();
+
+ getLimitSpy.and.returnValue(['3']);
+ expect(component.isPartialList()).toBeFalsy();
+ });
+
+ it('setInstanceVersion should work without instance version', () => {
fixture = TestBed.createComponent(SubsystemListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
+ component.setInstanceVersion();
+ expect(TestBed.get(Router).navigateByUrl).toHaveBeenCalledWith('/INST');
+ });
+});
+
+describe('SubsystemListComponent (with instance version)', () => {
+ let component: SubsystemListComponent;
+ let fixture: ComponentFixture;
+ let getInstanceSpy;
+ let getInstancesSpy;
+ let subsystemsService: SubsystemsService;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ SubsystemListComponent,
+ HeaderStubComponent,
+ MessagesStubComponent,
+ SearchStubComponent,
+ SubsystemItemStubComponent
+ ],
+ imports: [
+ FormsModule,
+ TranslateModule.forRoot(),
+ HttpClientModule
+ ],
+ providers: [
+ { provide: ActivatedRoute, useValue: {
+ params: of({
+ instance: 'INST'
+ }),
+ snapshot: {
+ queryParams: {
+ at: '12345'
+ }
+ }
+ }},
+ { provide: Router, useValue: {
+ events: of(new Scroll(null, [11, 12], null)),
+ navigateByUrl: jasmine.createSpy('navigateByUrl')
+ }},
+ { provide: AppConfig, useClass: AppConfigMock }
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ subsystemsService = TestBed.get(SubsystemsService);
+ getInstanceSpy = spyOn(subsystemsService, 'getInstance').and.returnValue('INST');
+ getInstancesSpy = spyOn(subsystemsService, 'getInstances').and.returnValue(['INST']);
+ spyOn(TestBed.get(ViewportScroller), 'scrollToPosition');
+ spyOn(subsystemsService, 'setInstance').and.returnValue(null);
+ spyOn(subsystemsService, 'getDefaultInstance').and.returnValue('DEFINST');
+ spyOn(TestBed.get(SubsystemsService), 'getApiUrlBase').and.returnValue('base');
});
it('should create', () => {
+ fixture = TestBed.createComponent(SubsystemListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
expect(component).toBeTruthy();
+ expect(component.instanceVersion).toBe('12345');
+ });
+
+ it('setInstanceVersion should work with instance version', () => {
+ fixture = TestBed.createComponent(SubsystemListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ component.setInstanceVersion();
+ expect(TestBed.get(Router).navigateByUrl).toHaveBeenCalledWith('/INST?at=12345');
+ });
+
+ it('switchInstance should reset instance version when instance does not change', () => {
+ fixture = TestBed.createComponent(SubsystemListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ component.instanceVersion = 'test';
+ component.switchInstance('INST');
+ expect(TestBed.get(Router).navigateByUrl).toHaveBeenCalledWith('/INST');
+ expect(component.instanceVersion).toBe('');
});
-});
\ No newline at end of file
+});
diff --git a/src/app/subsystem-list/subsystem-list.component.ts b/src/app/subsystem-list/subsystem-list.component.ts
index d1edb7954c22a340484bb42f1ae70c07baabfc50..109badac79ca46e690aa2028527d04a1205ff510 100644
--- a/src/app/subsystem-list/subsystem-list.component.ts
+++ b/src/app/subsystem-list/subsystem-list.component.ts
@@ -1,27 +1,29 @@
-import { Component, OnInit, OnDestroy } from '@angular/core';
+import { Component, OnInit, AfterViewInit, OnDestroy } from '@angular/core';
import { Subsystem } from '../subsystem';
-import { MethodsService } from '../methods.service';
+import { SubsystemsService } from '../subsystems.service';
import { ActivatedRoute, Router, Scroll } from '@angular/router';
-import { Subscription } from 'rxjs';
+import { Subscription, BehaviorSubject } from 'rxjs';
import { ViewportScroller } from '@angular/common';
import { filter } from 'rxjs/operators';
+import { InstanceVersion } from '../instance-version';
@Component({
selector: 'app-subsystem-list',
- templateUrl: './subsystem-list.component.html',
- styleUrls: ['./subsystem-list.component.css']
+ templateUrl: './subsystem-list.component.html'
})
-export class SubsystemListComponent implements OnInit, OnDestroy {
- subsystems: Subsystem[]
- message: string = ''
- scrollPosition: [number, number] = [0, 0]
- routerScrollSubscription: Subscription
- routeSubscription: Subscription
- updatedSubscription: Subscription
- warningsSubscription: Subscription
+export class SubsystemListComponent implements OnInit, AfterViewInit, OnDestroy {
+ message: string;
+ scrollSubject: BehaviorSubject = new BehaviorSubject(null);
+ routerScrollSubscription: Subscription;
+ routeSubscription: Subscription;
+ warningsSubscription: Subscription;
+ scrollSubjectSubscription: Subscription;
+ filteredSubsystems: BehaviorSubject;
+ instanceVersions: BehaviorSubject;
+ instanceVersion: string;
constructor(
- private methodsService: MethodsService,
+ private subsystemsService: SubsystemsService,
private route: ActivatedRoute,
private router: Router,
private viewportScroller: ViewportScroller
@@ -29,77 +31,108 @@ export class SubsystemListComponent implements OnInit, OnDestroy {
// Geting previous scroll position
this.routerScrollSubscription = this.router.events.pipe(
filter(e => e instanceof Scroll)
- ).subscribe(e => {
+ ).subscribe(async (e) => {
if ((e as Scroll).position) {
- this.scrollPosition = (e as Scroll).position;
- } else {
- this.scrollPosition = [0, 0];
+ this.scrollSubject.next((e as Scroll).position);
}
});
}
+ getInstance(): string {
+ return this.subsystemsService.getInstance();
+ }
+
+ getInstances(): string[] {
+ return this.subsystemsService.getInstances();
+ }
+
+ switchInstance(instance: string): void {
+ this.router.navigateByUrl('/' + instance);
+ // Reloading data if clicked on the current instance
+ if (this.subsystemsService.getInstance() === instance) {
+ this.instanceVersion = '';
+ this.subsystemsService.setInstance(instance, this.instanceVersion);
+ }
+ }
+
+ getApiUrl(): string {
+ return this.subsystemsService.getApiUrl();
+ }
+
+ isPartialList(): boolean {
+ const limit = parseInt(this.subsystemsService.getLimit(), 10);
+ if (isNaN(limit)) {
+ // limit is "all" (or some faulty string)
+ return false;
+ } else if (this.filteredSubsystems.value.length < limit) {
+ return false;
+ } else {
+ // If subsystems length == limit then we still asume it is partial
+ return true;
+ }
+ }
+
+ scrollToTop() {
+ this.viewportScroller.scrollToPosition([0, 0]);
+ }
+
+ setInstanceVersion() {
+ // This will update URL without triggering route.params
+ this.router.navigateByUrl(
+ '/' + this.subsystemsService.getInstance()
+ + (this.instanceVersion ? '?at=' + this.instanceVersion : ''));
+ this.subsystemsService.setInstance(this.subsystemsService.getInstance(), this.instanceVersion);
+ }
+
ngOnInit() {
// Reset message on page load
- this.message = ''
+ this.message = '';
+
+ this.filteredSubsystems = this.subsystemsService.filteredSubsystemsSubject;
+ this.instanceVersions = this.subsystemsService.instanceVersionsSubject;
// Service will tell when data loading failed!
- this.warningsSubscription = this.methodsService.warnings.subscribe(signal => {
- this.message = signal
+ this.warningsSubscription = this.subsystemsService.warnings.subscribe(signal => {
+ this.message = signal;
});
-
+
this.routeSubscription = this.route.params.subscribe( params => {
// Reset message on navigation
- this.message = ''
+ this.message = '';
// Redirect to default instance if instance is empty or invalid
- if (!this.methodsService.getInstances().includes(params['instance'])) {
- this.router.navigateByUrl('/' + this.methodsService.getDefaultInstance())
- return
+ if (!this.subsystemsService.getInstances().includes(params.instance)) {
+ this.router.navigateByUrl('/' + this.subsystemsService.getDefaultInstance());
+ return;
}
- // Only reload on switching of instance or when no instance is selected yet on service side
- if (this.getInstance() == '' || this.getInstance() != params['instance']) {
- this.methodsService.setInstance(params['instance'] ? params['instance'] : this.methodsService.getDefaultInstance())
+
+ // Set selected instance version
+ if (this.route.snapshot && this.route.snapshot.queryParams.at) {
+ this.instanceVersion = this.route.snapshot.queryParams.at;
+ } else {
+ this.instanceVersion = '';
}
- });
- // Service will tell when updated data is available!
- this.updatedSubscription = this.methodsService.subsystemsUpdated.subscribe(signal => {
- this.getMethods();
+ // Only reload on switching of instance or when no instance is selected yet on service side
+ if (this.getInstance() === '' || this.getInstance() !== params.instance) {
+ this.subsystemsService.setInstance(params.instance, this.instanceVersion);
+ }
});
- // If json data is loaded update event will not be emited.
- // This line must be after subscription (data may be changed while we start subscription)
- this.getMethods();
}
ngAfterViewInit() {
// Restoring scroll position
- this.viewportScroller.scrollToPosition(this.scrollPosition);
- // TODO: what if this.scrollPosition is not ready yet?
- /*this.routerScrollSubscription.add(() => {
- this.viewportScroller.scrollToPosition(this.scrollPosition);
- })*/
+ this.scrollSubjectSubscription = this.scrollSubject.subscribe( position => {
+ if (position) {
+ this.viewportScroller.scrollToPosition(position);
+ }
+ });
}
ngOnDestroy() {
- this.routerScrollSubscription.unsubscribe()
- this.routeSubscription.unsubscribe()
- this.updatedSubscription.unsubscribe()
- this.warningsSubscription.unsubscribe()
- }
-
- getInstance(): string {
- return this.methodsService.getInstance()
- }
-
- getInstances(): string[] {
- return this.methodsService.getInstances()
- }
-
- getMethods(): void {
- this.subsystems = this.methodsService.getMethods()
- }
-
- switchInstance(instance: string): void {
- this.router.navigateByUrl('/' + instance)
+ this.routerScrollSubscription.unsubscribe();
+ this.routeSubscription.unsubscribe();
+ this.warningsSubscription.unsubscribe();
+ this.scrollSubjectSubscription.unsubscribe();
}
}
diff --git a/src/app/subsystem/subsystem.component.css b/src/app/subsystem/subsystem.component.css
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/app/subsystem/subsystem.component.html b/src/app/subsystem/subsystem.component.html
index 0662f7a13240021ab211d85c1b5a7fb86e68b5d2..89829678aaa97f0413531af11e1879fe320ea9a0 100644
--- a/src/app/subsystem/subsystem.component.html
+++ b/src/app/subsystem/subsystem.component.html
@@ -1,15 +1,13 @@
-{{'subsystem.heading' | translate:{"subsystem": subsystemId, "instance": getInstance()} }}
+{{'subsystem.heading' | translate:{"subsystem": subsystemId, "instance": getInstance() === "" ? paramsInstance : getInstance()} }}
-
- {{message}}
-
+
-
+
+
+
+
+
+
diff --git a/src/app/subsystem/subsystem.component.spec.ts b/src/app/subsystem/subsystem.component.spec.ts
index a39dc9768e8f14977bbbff2713e62dd5db393497..32b6ed7c468168cdaa186767c73d11fea2bdbb03 100644
--- a/src/app/subsystem/subsystem.component.spec.ts
+++ b/src/app/subsystem/subsystem.component.spec.ts
@@ -1,63 +1,240 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
-import { Component } from '@angular/core';
+import { Component, Input } from '@angular/core';
import { SubsystemComponent } from './subsystem.component';
-import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientModule } from '@angular/common/http';
-//import { ViewportScroller } from '@angular/common';
-//import { MethodsService } from '../methods.service';
-import { ActivatedRoute } from '@angular/router';
-import { of } from 'rxjs';
+import { Router, ActivatedRoute, Scroll } from '@angular/router';
+import { of, BehaviorSubject } from 'rxjs';
+import { SubsystemsService } from '../subsystems.service';
+import { ViewportScroller } from '@angular/common';
+import { AppConfigMock } from 'src/app/app.config-mock';
+import { AppConfig } from 'src/app/app.config';
@Component({selector: 'app-header', template: ''})
class HeaderStubComponent {}
+@Component({selector: 'app-messages', template: ''})
+class MessagesStubComponent {
+ @Input() message: string;
+ @Input() subsystemId: string;
+}
describe('SubsystemComponent', () => {
let component: SubsystemComponent;
let fixture: ComponentFixture
;
+ let getInstanceSpy;
+ let getInstancesSpy;
+ let subsystemsService: SubsystemsService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
SubsystemComponent,
- HeaderStubComponent
+ HeaderStubComponent,
+ MessagesStubComponent
],
imports: [
- TranslateModule.forRoot(),
- HttpClientModule,
- RouterTestingModule
- ]/*,
+ TranslateModule.forRoot(),
+ HttpClientModule
+ ],
providers: [
- MethodsService
- ]*/
+ { provide: ActivatedRoute, useValue: {
+ params: of({
+ instance: 'INST',
+ class: 'CLASS',
+ member: 'MEMBER',
+ subsystem: 'SYSTEM'
+ })
+ }},
+ { provide: Router, useValue: {
+ events: of(new Scroll(null, [11, 12], null)),
+ navigateByUrl: jasmine.createSpy('navigateByUrl')
+ }},
+ { provide: AppConfig, useClass: AppConfigMock }
+ ]
})
.compileComponents();
}));
beforeEach(() => {
- // Mocks and spies
- TestBed.get(ActivatedRoute).params = of({
- "instance": "EE",
- "class": "CLASS",
- "member": "MEMBER",
- "subsystem": "SYSTEM"
- })
- //spyOn(TestBed.get(ViewportScroller), "scrollToPosition").and.callFake(() => {});
+ subsystemsService = TestBed.get(SubsystemsService);
+ getInstanceSpy = spyOn(subsystemsService, 'getInstance').and.returnValue('INST');
+ getInstancesSpy = spyOn(subsystemsService, 'getInstances').and.returnValue(['INST']);
+ spyOn(TestBed.get(ViewportScroller), 'scrollToPosition');
+ spyOn(subsystemsService, 'setInstance').and.returnValue(null);
+ spyOn(TestBed.get(SubsystemsService), 'getApiUrlBase').and.returnValue('base');
+ subsystemsService.subsystemsSubject = new BehaviorSubject([
+ {
+ memberClass: '',
+ subsystemCode: '',
+ xRoadInstance: '',
+ subsystemStatus: '',
+ memberCode: '',
+ fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
+ methods: []
+ }
+ ]);
+ });
+ it('should create', () => {
fixture = TestBed.createComponent(SubsystemComponent);
component = fixture.componentInstance;
fixture.detectChanges();
+ expect(component).toBeTruthy();
});
- /*afterEach(() => {
- TestBed.resetTestEnvironment()
- });*/
+ it('should detect incorrect instance', () => {
+ getInstancesSpy.and.returnValue(['XXX']);
+ fixture = TestBed.createComponent(SubsystemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ expect(component.message).toBe('subsystem.incorrectInstanceWarning');
+ });
- it('should create', () => {
- expect(component).toBeTruthy();
+ it('should detect when instance is not selected', () => {
+ getInstanceSpy.and.returnValue('');
+ fixture = TestBed.createComponent(SubsystemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ expect(subsystemsService.setInstance).toHaveBeenCalledWith('INST', '');
+ });
+
+ it('should detect change instance', () => {
+ getInstanceSpy.and.returnValue('INST2');
+ getInstancesSpy.and.returnValue(['INST', 'INST2']);
+ fixture = TestBed.createComponent(SubsystemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ expect(subsystemsService.setInstance).toHaveBeenCalledWith('INST', '');
});
- /*it('scrollToPosition was called', async(() => {
- expect(TestBed.get(ViewportScroller).scrollToPosition).toHaveBeenCalledWith([0, 0])
- }));*/
+ it('should detect incorrect subsystem', () => {
+ subsystemsService.subsystemsSubject = new BehaviorSubject([
+ {
+ memberClass: '',
+ subsystemCode: '',
+ xRoadInstance: '',
+ subsystemStatus: '',
+ memberCode: '',
+ fullSubsystemName: 'INST/CLASS/MEMBER2/SYSTEM',
+ methods: []
+ }
+ ]);
+ fixture = TestBed.createComponent(SubsystemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ expect(component.message).toBe('subsystem.subsystemNotFoundWarning');
+ });
+
+ it('should scroll to position', () => {
+ fixture = TestBed.createComponent(SubsystemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ expect(TestBed.get(ViewportScroller).scrollToPosition).toHaveBeenCalledWith([11, 12]);
+ });
+
+ it('getApiUrlBase should work', () => {
+ fixture = TestBed.createComponent(SubsystemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ component.getApiUrlBase();
+ expect(subsystemsService.getApiUrlBase).toHaveBeenCalled();
+ });
+
+ it('goToList should work', () => {
+ fixture = TestBed.createComponent(SubsystemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ component.goToList();
+ expect(TestBed.get(Router).navigateByUrl).toHaveBeenCalledWith('/INST');
+ });
+
+ it('should receive service warnings', () => {
+ fixture = TestBed.createComponent(SubsystemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ subsystemsService.warnings.emit('WARN');
+ expect(component.message).toBe('WARN');
+ });
+
+ it('scrollToTop should work', () => {
+ fixture = TestBed.createComponent(SubsystemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ const spy = TestBed.get(ViewportScroller).scrollToPosition;
+ spy.calls.reset();
+ component.scrollToTop();
+ expect(spy).toHaveBeenCalledWith([0, 0]);
+ });
+});
+
+
+describe('SubsystemComponent (with instance version)', () => {
+ let component: SubsystemComponent;
+ let fixture: ComponentFixture;
+ let getInstanceSpy;
+ let getInstancesSpy;
+ let subsystemsService: SubsystemsService;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ SubsystemComponent,
+ HeaderStubComponent,
+ MessagesStubComponent
+ ],
+ imports: [
+ TranslateModule.forRoot(),
+ HttpClientModule
+ ],
+ providers: [
+ { provide: ActivatedRoute, useValue: {
+ params: of({
+ instance: 'INST',
+ class: 'CLASS',
+ member: 'MEMBER',
+ subsystem: 'SYSTEM'
+ }),
+ snapshot: {
+ queryParams: {
+ at: '12345'
+ }
+ }
+ }},
+ { provide: Router, useValue: {
+ events: of(new Scroll(null, [11, 12], null)),
+ navigateByUrl: jasmine.createSpy('navigateByUrl')
+ }},
+ { provide: AppConfig, useClass: AppConfigMock }
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ subsystemsService = TestBed.get(SubsystemsService);
+ getInstanceSpy = spyOn(subsystemsService, 'getInstance').and.returnValue('INST');
+ getInstancesSpy = spyOn(subsystemsService, 'getInstances').and.returnValue(['INST']);
+ spyOn(TestBed.get(ViewportScroller), 'scrollToPosition');
+ spyOn(subsystemsService, 'setInstance').and.returnValue(null);
+ spyOn(TestBed.get(SubsystemsService), 'getApiUrlBase').and.returnValue('base');
+ subsystemsService.subsystemsSubject = new BehaviorSubject([
+ {
+ memberClass: '',
+ subsystemCode: '',
+ xRoadInstance: '',
+ subsystemStatus: '',
+ memberCode: '',
+ fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
+ methods: []
+ }
+ ]);
+ });
+
+ it('goToList should work with instance version', () => {
+ fixture = TestBed.createComponent(SubsystemComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ component.goToList();
+ expect(TestBed.get(Router).navigateByUrl).toHaveBeenCalledWith('/INST?at=12345');
+ });
});
diff --git a/src/app/subsystem/subsystem.component.ts b/src/app/subsystem/subsystem.component.ts
index fae97582377023207e49f3f1f0c432e1f2e7d9f6..ff03b970080584e4f38f387539daa806716583ce 100644
--- a/src/app/subsystem/subsystem.component.ts
+++ b/src/app/subsystem/subsystem.component.ts
@@ -1,114 +1,129 @@
-import { Component, OnInit, OnDestroy } from '@angular/core';
-import { MethodsService } from '../methods.service';
+import { Component, OnInit, AfterViewInit, OnDestroy } from '@angular/core';
+import { SubsystemsService } from '../subsystems.service';
import { Subsystem } from '../subsystem';
import { ActivatedRoute, Router, Scroll } from '@angular/router';
-import { Subscription } from 'rxjs';
+import { Subscription, BehaviorSubject } from 'rxjs';
import { ViewportScroller } from '@angular/common';
-import { filter, take } from 'rxjs/operators';
+import { filter } from 'rxjs/operators';
@Component({
selector: 'app-subsystem',
- templateUrl: './subsystem.component.html',
- styleUrls: ['./subsystem.component.css']
+ templateUrl: './subsystem.component.html'
})
-export class SubsystemComponent implements OnInit, OnDestroy {
- subsystem: Subsystem
- subsystemId: string
- message: string = ''
- scrollPosition: [number, number] = [0, 0]
- routerScrollSubscription: Subscription
- routeSubscription: Subscription
- updatedSubscription: Subscription
- warningsSubscription: Subscription
+export class SubsystemComponent implements OnInit, AfterViewInit, OnDestroy {
+ subsystemId = '';
+ message = '';
+ // Contains instance from route.params (for displaying warning)
+ paramsInstance = '';
+ private scrollSubject: BehaviorSubject = new BehaviorSubject(null);
+ private routerScrollSubscription: Subscription;
+ private routeSubscription: Subscription;
+ private warningsSubscription: Subscription;
+ private scrollSubjectSubscription: Subscription;
+ private subsystemsSubscription: Subscription;
+ private instanceVersion: string;
+ subsystemSubject: BehaviorSubject = new BehaviorSubject(null);
constructor(
- private methodsService: MethodsService,
+ private subsystemsService: SubsystemsService,
private route: ActivatedRoute,
private router: Router,
private viewportScroller: ViewportScroller
) {
// Geting previous scroll position
this.routerScrollSubscription = this.router.events.pipe(
- filter(e => e instanceof Scroll),
- take(1)
- ).subscribe(e => {
+ filter(e => e instanceof Scroll)
+ ).subscribe(async (e) => {
if ((e as Scroll).position) {
- this.scrollPosition = (e as Scroll).position;
- } else {
- this.scrollPosition = [0, 0];
+ this.scrollSubject.next((e as Scroll).position);
}
});
}
- private checkSubsystem() {
- // Do not overwrite previous warnings
- if (!this.subsystem && !this.message) {
- this.message = 'Subsystem "' + this.subsystemId + '" cannot be found!'
- }
-}
+ private getSubsystem(subsystems: Subsystem[], name: string): Subsystem {
+ return subsystems.find((element) => {
+ return element.fullSubsystemName === name;
+ });
+ }
+
+ getInstance(): string {
+ return this.subsystemsService.getInstance();
+ }
+
+ getApiUrlBase(): string {
+ return this.subsystemsService.getApiUrlBase();
+ }
+
+ goToList(): void {
+ this.router.navigateByUrl(
+ '/' + this.subsystemsService.getInstance()
+ + (this.instanceVersion ? '?at=' + this.instanceVersion : '')
+ );
+ }
+
+ scrollToTop() {
+ this.viewportScroller.scrollToPosition([0, 0]);
+ }
ngOnInit() {
// Reset message on page load
- this.message = ''
+ this.message = '';
// Service will tell when data loading failed!
- this.warningsSubscription = this.methodsService.warnings.subscribe(signal => {
- this.message = signal
+ this.warningsSubscription = this.subsystemsService.warnings.subscribe(signal => {
+ this.message = signal;
});
this.routeSubscription = this.route.params.subscribe( params => {
// Checking if instance is correct
- if (!this.methodsService.getInstances().includes(params['instance'])) {
- this.message = 'Incorrect instance!'
- return
+ if (!this.subsystemsService.getInstances().includes(params.instance)) {
+ this.paramsInstance = params.instance;
+ this.message = 'subsystem.incorrectInstanceWarning';
+ return;
}
- this.subsystemId = params['instance'] + '/' + params['class'] + '/' + params['member'] + '/' + params['subsystem']
- // Only reload on switching of instance or when no instance is selected yet on service side
- if (this.getInstance() == '' || this.getInstance() != params['instance']) {
- this.methodsService.setInstance(params['instance'] ? params['instance'] : this.methodsService.getDefaultInstance())
+
+ // Set selected instance version
+ if (this.route.snapshot && this.route.snapshot.queryParams.at) {
+ this.instanceVersion = this.route.snapshot.queryParams.at;
+ } else {
+ this.instanceVersion = '';
}
- });
- // Service will tell when data has finished loading
- this.updatedSubscription = this.methodsService.subsystemsUpdated.subscribe(signal => {
- this.subsystem = this.methodsService.getSubsystem(this.subsystemId)
- if (this.methodsService.isLoadingDone() && !this.methodsService.isLoadingError()) {
- this.checkSubsystem()
+ this.subsystemId = params.instance + '/' + params.class + '/' + params.member + '/' + params.subsystem;
+
+ // Only reload on switching of instance or when no instance is selected yet on service side
+ if (this.getInstance() === '' || this.getInstance() !== params.instance) {
+ // this.subsystemsService.setInstance(params.instance ? params.instance : this.subsystemsService.getDefaultInstance());
+ this.subsystemsService.setInstance(params.instance, this.instanceVersion);
}
+
+ this.subsystemsSubscription = this.subsystemsService.subsystemsSubject.subscribe(subsystems => {
+ const subsystem = this.getSubsystem(subsystems, this.subsystemId);
+ if (!subsystem && !this.message && subsystems.length) {
+ this.message = 'subsystem.subsystemNotFoundWarning';
+ } else {
+ this.subsystemSubject.next(subsystem);
+ }
+ });
});
- // If json data is loaded update event will not be emited.
- // This line must be after subscription (data may be changed while we start subscription)
- if (this.subsystemId && this.methodsService.isLoadingDone() && !this.methodsService.isLoadingError()) {
- this.subsystem = this.methodsService.getSubsystem(this.subsystemId)
- this.checkSubsystem()
- }
}
ngAfterViewInit() {
// Restoring scroll position
- this.viewportScroller.scrollToPosition(this.scrollPosition);
- // TODO: what if this.scrollPosition is not ready yet?
- /*this.routerScrollSubscription.add(() => {
- this.viewportScroller.scrollToPosition(this.scrollPosition);
- })*/
+ this.scrollSubjectSubscription = this.scrollSubject.subscribe( position => {
+ if (position) {
+ this.viewportScroller.scrollToPosition(position);
+ }
+ });
}
ngOnDestroy() {
- this.routerScrollSubscription.unsubscribe()
- this.routeSubscription.unsubscribe()
- this.updatedSubscription.unsubscribe()
- this.warningsSubscription.unsubscribe()
- }
-
- getInstance(): string {
- return this.methodsService.getInstance()
- }
-
- getApiUrlBase(): string {
- return this.methodsService.getApiUrlBase()
- }
-
- goToList(): void {
- this.router.navigateByUrl('/' + this.methodsService.getInstance())
+ this.routerScrollSubscription.unsubscribe();
+ this.routeSubscription.unsubscribe();
+ this.warningsSubscription.unsubscribe();
+ this.scrollSubjectSubscription.unsubscribe();
+ if (this.subsystemsSubscription) {
+ this.subsystemsSubscription.unsubscribe();
+ }
}
}
diff --git a/src/app/subsystems.service.spec.ts b/src/app/subsystems.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0106e015ac2c04400b14fd1cb79914f17754d1a1
--- /dev/null
+++ b/src/app/subsystems.service.spec.ts
@@ -0,0 +1,381 @@
+import { SubsystemsService } from './subsystems.service';
+import { of, defer } from 'rxjs';
+import { Subsystem } from './subsystem';
+import { Method } from './method';
+import { HttpErrorResponse } from '@angular/common/http';
+import { tick, fakeAsync } from '@angular/core/testing';
+import { AppConfigMock } from './app.config-mock';
+import { InstanceVersion } from './instance-version';
+
+describe('SubsystemsService', () => {
+ let httpClientSpy: { get: jasmine.Spy };
+ let service: SubsystemsService;
+ let config: AppConfigMock;
+
+ beforeEach(() => {
+ httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
+ config = new AppConfigMock(httpClientSpy as any);
+ service = new SubsystemsService(httpClientSpy as any, config);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+
+ it('should set instance on HTTP OK', fakeAsync(() => {
+ const sourceSubsystems = [
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER',
+ methods: [
+ {
+ methodStatus: 'OK',
+ serviceCode: 'SERVICE',
+ wsdl: 'URL',
+ serviceVersion: 'VER'
+ }
+ ]
+ },
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER2',
+ methods: []
+ }
+ ];
+ const expectedSubsystems = [
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER',
+ fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
+ methods: [
+ {
+ methodStatus: 'OK',
+ serviceCode: 'SERVICE',
+ wsdl: 'URL',
+ serviceVersion: 'VER',
+ fullMethodName: 'INST/CLASS/MEMBER/SYSTEM/SERVICE/VER'
+ }
+ ]
+ },
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER2',
+ fullSubsystemName: 'INST/CLASS/MEMBER2/SYSTEM',
+ methods: []
+ }
+ ];
+ httpClientSpy.get.and.returnValue(of(sourceSubsystems));
+ // Setting value to test resetting of values
+ service.subsystemsSubject.next([new Subsystem()]);
+ service.filteredSubsystemsSubject.next([new Subsystem()]);
+ // Disabling updateInstanceVersions()
+ service.updateInstanceVersions = () => {};
+
+ service.setInstance('EE', '');
+ // Waiting for asynchronous work
+ tick();
+ expect(httpClientSpy.get).toHaveBeenCalledWith('https://www.x-tee.ee/catalogue/EE/wsdls/index.json');
+ expect(service.subsystemsSubject.value).toEqual(expectedSubsystems);
+ // No filters yet
+ expect(service.filteredSubsystemsSubject.value).toEqual(expectedSubsystems);
+
+ service.setInstance('EE', '12345');
+ // Waiting for asynchronous work
+ tick();
+ expect(httpClientSpy.get).toHaveBeenCalledWith('https://www.x-tee.ee/catalogue/EE/wsdls/index_12345.json');
+ }));
+
+ it('should set instance on HTTP ERROR', fakeAsync(() => {
+ const errorResponse = new HttpErrorResponse({error: 'error', status: 404, statusText: 'Not Found'});
+ httpClientSpy.get.and.returnValue(defer(() => Promise.reject(errorResponse)));
+ // Setting value to test resetting of values
+ service.subsystemsSubject.next([new Subsystem()]);
+ service.filteredSubsystemsSubject.next([new Subsystem()]);
+ // Disabling updateInstanceVersions()
+ service.updateInstanceVersions = () => {};
+
+ service.setInstance('EE', '');
+ // Waiting for asynchronous work
+ tick();
+ expect(httpClientSpy.get).toHaveBeenCalledWith('https://www.x-tee.ee/catalogue/EE/wsdls/index.json');
+ expect(service.subsystemsSubject.value).toEqual([]);
+ expect(service.filteredSubsystemsSubject.value).toEqual([]);
+ }));
+
+ it('should set instance versions on HTTP OK', fakeAsync(() => {
+ const sourceHistory = [
+ {
+ reportTime: '2019-04-15 08:01:41',
+ reportPath: 'index_20190415080141.json'
+ },
+ {
+ reportTime: '2019-04-14 08:01:37',
+ reportPath: 'index_FAIL.json'
+ }
+ ];
+ const expectedInstanceVersions = [
+ {
+ reportTime: '2019-04-15 08:01:41',
+ reportTimeCompact: '20190415080141',
+ reportPath: 'index_20190415080141.json'
+ }
+ ];
+ httpClientSpy.get.and.returnValue(of(sourceHistory));
+ // Setting value to test resetting of values
+ service.instanceVersionsSubject.next([new InstanceVersion()]);
+ // Disabling updateSubsystems()
+ service.updateSubsystems = () => {};
+
+ service.setInstance('EE', '');
+ // Waiting for asynchronous work
+ tick();
+ expect(httpClientSpy.get).toHaveBeenCalledWith('https://www.x-tee.ee/catalogue/EE/wsdls/history.json');
+ expect(service.instanceVersionsSubject.value).toEqual(expectedInstanceVersions);
+
+ const longHistory = [];
+ for (let i = 0; i < config.getConfig('HISTORY_LIMIT') + 1; i++) {
+ longHistory.push(
+ {
+ reportTime: '2019-04-15 08:01:41',
+ reportPath: 'index_20190415080141.json'
+ }
+ );
+ }
+ httpClientSpy.get.and.returnValue(of(longHistory));
+ service.setInstance('EE');
+ // Waiting for asynchronous work
+ tick();
+ expect(longHistory.length).toBe(config.getConfig('HISTORY_LIMIT') + 1);
+ expect(service.instanceVersionsSubject.value.length).toEqual(config.getConfig('HISTORY_LIMIT'));
+
+ }));
+
+ it('should set instance versions on HTTP ERROR', fakeAsync(() => {
+ const errorResponse = new HttpErrorResponse({error: 'error', status: 404, statusText: 'Not Found'});
+ httpClientSpy.get.and.returnValue(defer(() => Promise.reject(errorResponse)));
+ // Setting value to test resetting of values
+ service.instanceVersionsSubject.next([new InstanceVersion()]);
+ // Disabling updateSubsystems()
+ service.updateSubsystems = () => {};
+
+ service.setInstance('EE');
+ // Waiting for asynchronous work
+ tick();
+ expect(httpClientSpy.get).toHaveBeenCalledWith('https://www.x-tee.ee/catalogue/EE/wsdls/history.json');
+ expect(service.instanceVersionsSubject.value).toEqual([]);
+ }));
+
+ it('should filter nonEmpty subsystems', () => {
+ const sourceSubsystems = [
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER',
+ fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
+ methods: [{} as Method]
+ },
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER2',
+ fullSubsystemName: 'INST/CLASS/MEMBER2/SYSTEM',
+ methods: []
+ }
+ ];
+ const expectedSubsystems = [
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER',
+ fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
+ methods: [{} as Method]
+ }
+ ];
+ service.subsystemsSubject.next(sourceSubsystems);
+ service.setNonEmpty(true);
+ expect(service.filteredSubsystemsSubject.value).toEqual(expectedSubsystems);
+ });
+
+ it('should set filter for subsystems', fakeAsync(() => {
+ const sourceSubsystems = [
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER',
+ fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
+ methods: [
+ {
+ methodStatus: 'OK',
+ serviceCode: 'SERVICE',
+ wsdl: 'URL',
+ serviceVersion: 'VER',
+ fullMethodName: 'INST/CLASS/MEMBER/SYSTEM/SERVICE/VER'
+ },
+ {
+ methodStatus: 'OK',
+ serviceCode: 'SERVICE2',
+ wsdl: 'URL',
+ serviceVersion: 'VER',
+ fullMethodName: 'INST/CLASS/MEMBER/SYSTEM/SERVICE2/VER'
+ }
+ ]
+ },
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER2',
+ fullSubsystemName: 'INST/CLASS/MEMBER2/SYSTEM',
+ methods: []
+ }
+ ];
+ const expectedSubsystems1 = [
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER2',
+ fullSubsystemName: 'INST/CLASS/MEMBER2/SYSTEM',
+ methods: []
+ }
+ ];
+ const expectedSubsystems2 = [
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER',
+ fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
+ methods: [
+ {
+ methodStatus: 'OK',
+ serviceCode: 'SERVICE2',
+ wsdl: 'URL',
+ serviceVersion: 'VER',
+ fullMethodName: 'INST/CLASS/MEMBER/SYSTEM/SERVICE2/VER'
+ }
+ ]
+ }
+ ];
+ service.subsystemsSubject.next(sourceSubsystems);
+
+ // Search member without methods
+ service.setFilter('MEMBER2');
+ // Waiting for a debounce time to apply filter
+ tick(config.getConfig('FILTER_DEBOUNCE'));
+ expect(service.filteredSubsystemsSubject.value).toEqual(expectedSubsystems1);
+
+ // Search member with multiple methods
+ service.setFilter('SERVICE2');
+ // Waiting for a debounce time to apply filter
+ tick(config.getConfig('FILTER_DEBOUNCE'));
+ expect(service.filteredSubsystemsSubject.value).toEqual(expectedSubsystems2);
+
+ // Search with limit
+ const sourceSubsystems2 = [];
+ for (let i = 0; i < config.getConfig('DEFAULT_LIMIT') + 1; i++) {
+ sourceSubsystems2.push(
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER' + i,
+ fullSubsystemName: 'INST/CLASS/MEMBER' + i + '/SYSTEM',
+ methods: []
+ }
+ );
+ }
+ service.subsystemsSubject.next(sourceSubsystems2);
+ service.setFilter('MEMBER');
+ // Waiting for a debounce time to apply filter
+ tick(config.getConfig('FILTER_DEBOUNCE'));
+ expect(service.filteredSubsystemsSubject.value.length).toEqual(config.getConfig('DEFAULT_LIMIT'));
+ }));
+
+ it('should set limit', () => {
+ const sourceSubsystems = [];
+ for (let i = 0; i < 51; i++) {
+ sourceSubsystems.push(
+ {
+ memberClass: 'CLASS',
+ subsystemCode: 'SYSTEM',
+ xRoadInstance: 'INST',
+ subsystemStatus: 'OK',
+ memberCode: 'MEMBER' + i,
+ fullSubsystemName: 'INST/CLASS/MEMBER' + i + '/SYSTEM',
+ methods: []
+ }
+ );
+ }
+ service.subsystemsSubject.next(sourceSubsystems);
+
+ service.setLimit('all');
+ expect(service.filteredSubsystemsSubject.value.length).toEqual(51);
+
+ service.setLimit('50');
+ expect(service.filteredSubsystemsSubject.value.length).toEqual(50);
+
+ service.setLimit('20');
+ expect(service.filteredSubsystemsSubject.value.length).toEqual(20);
+
+ service.setLimit('10');
+ expect(service.filteredSubsystemsSubject.value.length).toEqual(10);
+
+ // Should set default limit of 10
+ service.setLimit('5');
+ expect(service.filteredSubsystemsSubject.value.length).toEqual(10);
+ });
+
+ it('getLimit should work', () => {
+ expect(service.getLimit()).toEqual(config.getConfig('DEFAULT_LIMIT').toString());
+
+ service.setLimit('all');
+ expect(service.getLimit()).toEqual('all');
+ });
+
+ it('getLimits should work', () => {
+ expect(service.getLimits()).toEqual(config.getConfig('LIMITS'));
+ });
+
+ it('getInstances should work', () => {
+ expect(service.getInstances()).toEqual(Object.keys(config.getConfig('INSTANCES')));
+ });
+
+ it('getDefaultInstance should work', () => {
+ expect(service.getDefaultInstance()).toEqual(Object.keys(config.getConfig('INSTANCES'))[0]);
+ });
+
+ it('getInstance should work', () => {
+ // Default value
+ expect(service.getInstance()).toEqual('');
+ });
+
+ it('getApiUrlBase should work', () => {
+ // Default value
+ expect(service.getApiUrlBase()).toEqual('');
+ });
+});
diff --git a/src/app/subsystems.service.ts b/src/app/subsystems.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d557da7c418e67f6e3482746011f2b52b6675f2c
--- /dev/null
+++ b/src/app/subsystems.service.ts
@@ -0,0 +1,235 @@
+import { Injectable, EventEmitter } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { of, BehaviorSubject, Subject } from 'rxjs';
+import { catchError, debounceTime, distinctUntilChanged } from 'rxjs/operators';
+import { Subsystem } from './subsystem';
+import { Method } from './method';
+import { AppConfig } from './app.config';
+import { InstanceVersion } from './instance-version';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class SubsystemsService {
+ private apiUrlBase = '';
+ private limit: number = this.config.getConfig('DEFAULT_LIMIT');
+ private nonEmpty = false;
+ private filter = '';
+ private instance = '';
+ private instanceVersion = '';
+ subsystemsSubject: BehaviorSubject = new BehaviorSubject([]);
+ filteredSubsystemsSubject: BehaviorSubject = new BehaviorSubject([]);
+ instanceVersionsSubject: BehaviorSubject = new BehaviorSubject([]);
+ private updateFilter = new Subject();
+
+ warnings: EventEmitter = new EventEmitter();
+
+ constructor(
+ private http: HttpClient,
+ private config: AppConfig
+ ) {
+ // Debouncing update of filter
+ this.updateFilter.pipe(
+ debounceTime(this.config.getConfig('FILTER_DEBOUNCE')),
+ distinctUntilChanged()
+ ).subscribe(() => {
+ this.updateFiltered();
+ });
+ }
+
+ private filteredSubsystems(): Subsystem[] {
+ const filtered: Subsystem[] = [];
+ let limit: number = this.limit;
+ for (let subsystem of this.subsystemsSubject.value) {
+ if (this.nonEmpty && !subsystem.methods.length) {
+ // Filtering out empty subsystems
+ continue;
+ }
+ if (
+ this.filter !== ''
+ && !subsystem.methods.length
+ ) {
+ // Subsystem without methods
+ if (subsystem.fullSubsystemName.toLowerCase().indexOf(this.filter.toLowerCase()) < 0) {
+ // Subsystem name does not match the filter
+ continue;
+ }
+ } else if (this.filter !== '') {
+ // Subsystem with methods
+ const filteredMethods: Method[] = [];
+ for (const method of subsystem.methods) {
+ if (method.fullMethodName.toLowerCase().indexOf(this.filter.toLowerCase()) >= 0) {
+ filteredMethods.push(method);
+ }
+ }
+ if (!filteredMethods.length) {
+ // No matching method names found
+ continue;
+ }
+ // Copy object to avoid overwriting methods array in subsystem object
+ subsystem = Object.assign(Object.create(subsystem), subsystem);
+ // Leaving only matcing methods
+ subsystem.methods = filteredMethods;
+ }
+ filtered.push(subsystem);
+ limit -= 1;
+ if (limit === 0) {
+ break;
+ }
+ }
+ return filtered;
+ }
+
+ private setFullNames(subsystems: Subsystem[]): Subsystem[] {
+ for (const subsystem of subsystems) {
+ subsystem.fullSubsystemName = subsystem.xRoadInstance
+ + '/' + subsystem.memberClass
+ + '/' + subsystem.memberCode
+ + '/' + subsystem.subsystemCode;
+ for (const method of subsystem.methods) {
+ method.fullMethodName = subsystem.fullSubsystemName
+ + '/' + method.serviceCode
+ + '/' + method.serviceVersion;
+ }
+ }
+ return subsystems;
+ }
+
+ private emitWarning(msg: string) {
+ this.warnings.emit(msg);
+ }
+
+ private updateFiltered() {
+ this.filteredSubsystemsSubject.next(this.filteredSubsystems());
+ }
+
+ getDefaultInstance(): string {
+ return Object.keys(this.config.getConfig('INSTANCES'))[0];
+ }
+
+ getInstances(): string[] {
+ return Object.keys(this.config.getConfig('INSTANCES'));
+ }
+
+ getInstance(): string {
+ return this.instance;
+ }
+
+ // Not "private" to be able to override in unit tests
+ updateSubsystems() {
+ // Reset only if has values (less refreshes)
+ if (this.subsystemsSubject.value.length) {
+ this.subsystemsSubject.next([]);
+ }
+ this.http.get(
+ this.apiUrlBase + (this.instanceVersion ? 'index_' + this.instanceVersion + '.json' : this.config.getConfig('API_SERVICE'))
+ ).pipe(
+ catchError( () => {
+ this.emitWarning('service.dataLoadingError');
+ // Let the app keep running by returning an empty result.
+ return of([]);
+ })
+ ).subscribe(subsystems => {
+ this.subsystemsSubject.next(this.setFullNames(subsystems));
+ this.updateFiltered();
+ });
+ }
+
+ // Not "private" to be able to override in unit tests
+ updateInstanceVersions() {
+ this.instanceVersionsSubject.next([]);
+ this.http.get(this.apiUrlBase + this.config.getConfig('API_HISTORY'))
+ .pipe(
+ catchError(() => {
+ // Let the app keep running by returning an empty result.
+ return of([]);
+ })
+ ).subscribe(history => {
+ const versions: InstanceVersion[] = [];
+ for (const version of history) {
+ const matches = version.reportPath.match(/index_(\d+).json/);
+ if (matches && matches.length === 2) {
+ version.reportTimeCompact = matches[1];
+ versions.push(version);
+ }
+ if (versions.length >= this.config.getConfig('HISTORY_LIMIT')) {
+ break;
+ }
+ }
+ this.instanceVersionsSubject.next(versions);
+ });
+ }
+
+ setInstance(instance: string, instanceVersion: string = '') {
+ this.instance = instance;
+ this.instanceVersion = instanceVersion;
+ this.apiUrlBase = this.config.getConfig('INSTANCES')[instance];
+
+ this.updateSubsystems();
+ this.updateInstanceVersions();
+ }
+
+ getApiUrlBase(): string {
+ return this.apiUrlBase;
+ }
+
+ getApiUrl(): string {
+ return this.apiUrlBase + this.config.getConfig('API_SERVICE');
+ }
+
+ getLimit(): string {
+ if (this.limit === this.config.getConfig('MAX_LIMIT')) {
+ return 'all';
+ }
+ return this.limit.toString();
+ }
+
+ getLimits(): object {
+ return this.config.getConfig('LIMITS');
+ }
+
+ setLimit(limit: string) {
+ const limits = this.config.getConfig('LIMITS');
+ let found = false;
+ for (const key of Object.keys(limits)) {
+ if (limit === key) {
+ this.limit = limits[key];
+ found = true;
+ break;
+ }
+ }
+ if (!found && limit === 'all') {
+ this.limit = this.config.getConfig('MAX_LIMIT');
+ found = true;
+ }
+ if (!found) {
+ this.limit = this.config.getConfig('DEFAULT_LIMIT');
+ }
+ this.updateFiltered();
+ }
+
+ getNonEmpty(): boolean {
+ return this.nonEmpty;
+ }
+
+ setNonEmpty(nonEmpty: boolean) {
+ this.nonEmpty = nonEmpty;
+ this.updateFiltered();
+ }
+
+ getfilter(): string {
+ return this.filter;
+ }
+
+ setFilter(filter: string) {
+ if (this.filter !== filter.trim()) {
+ this.filter = filter.trim();
+ // Debouncing update of filter
+ this.updateFilter.next(this.filter);
+ }
+ }
+
+ getInstanceVersion(): string {
+ return this.instanceVersion;
+ }
+}
diff --git a/src/assets/config.json b/src/assets/config.json
new file mode 100644
index 0000000000000000000000000000000000000000..795d56d686f3643020c4e41478cfb1b86be5b701
--- /dev/null
+++ b/src/assets/config.json
@@ -0,0 +1,23 @@
+{
+ "MAX_LIMIT": 1000000,
+ "DEFAULT_LIMIT": 10,
+ "LIMITS": {
+ "10": 10,
+ "20": 20,
+ "50": 50
+ },
+ "INSTANCES": {
+ "EE": "https://www.x-tee.ee/catalogue/EE/wsdls/",
+ "ee-test": "https://www.x-tee.ee/catalogue/ee-test/wsdls/",
+ "ee-dev": "https://www.x-tee.ee/catalogue/ee-dev/wsdls/"
+ },
+ "API_SERVICE": "index.json",
+ "API_HISTORY": "history.json",
+ "HISTORY_LIMIT": 30,
+ "LANGUAGES": {
+ "EST": "est",
+ "ENG": "eng"
+ },
+ "PREVIEW_SIZE": 5,
+ "FILTER_DEBOUNCE": 200
+}
diff --git a/src/assets/i18n/eng.json b/src/assets/i18n/eng.json
index f84c5f3be2cda591b85c6d868e2d53eeb8cb6682..1b17f9c82a5a336f3723ef7097de8b26d8befe09 100644
--- a/src/assets/i18n/eng.json
+++ b/src/assets/i18n/eng.json
@@ -1,35 +1,47 @@
{
- "index": {
- "title": "X-tee Subsystems and Methods catalogue"
+ "index": {
+ "title": "X-tee Subsystems and Methods catalogue"
+ },
+ "service": {
+ "dataLoadingError": "Error while loading data from server!"
+ },
+ "subsystemList": {
+ "heading": "Catalogue of all X-tee subsystems with methods and WSDL descriptions",
+ "intro": {
+ "p1": "This catalogue is generated by making getWsdl requests from RIA's Monitoring Security Server to all X-tee subsystems.",
+ "p2": "Subsystems with the Empty icon have no X-Tee services available.",
+ "p3": "Subsystems with the Error icon either could not be reached by RIA's Monitoring Security Server or there was some other error during the request.",
+ "p4": "Latest catalogue version in JSON form: JSON",
+ "p5": "Support: help@ria.ee"
},
- "subsystemList": {
- "heading": "Catalogue of all X-tee subsystems with methods and WSDL descriptions",
- "intro": {
- "p1": "This catalogue is generated by making getWsdl requests from RIA's Monitoring Security Server to all X-tee subsystems.",
- "p2": "Subsystems with the Empty icon have no X-Tee services available.",
- "p3": "Subsystems with the Error icon either could not be reached by RIA's Monitoring Security Server or there was some other error during the request.",
- "p4": "Support: help@ria.ee"
- },
- "selectInstance": "Select X-tee instance"
+ "moreSubsystems": "To see more subsystems change amount of subsystems to display or filter by subsystem or service name.",
+ "selectInstance": "Select X-tee instance",
+ "selectVersion": "Select methods catalogue version",
+ "latestVersion": "Latest version"
+ },
+ "search": {
+ "searchFilters": "Search filters",
+ "nameFilter": "Filter by name",
+ "limit": "Amount of subsystems to display",
+ "nonEmpty": "Show only producer subsystems",
+ "allOption": "All"
+ },
+ "subsystem": {
+ "heading": "Subsystem {{subsystem}} with methods and WSDL descriptions for X-tee instance \"{{instance}}\"",
+ "intro": {
+ "p1": "Support: help@ria.ee"
},
- "search": {
- "searchFilters": "Search filters",
- "nameFilter": "Filter by name",
- "limit": "Amount of subsystems to display",
- "nonEmpty": "Show only producer subsystems",
- "allOption": "All"
- },
- "subsystem": {
- "heading": "Subsystem {{subsystem}} with methods and WSDL descriptions for X-tee instance \"{{instance}}\"",
- "intro": {
- "p1": "Support: help@ria.ee"
- },
- "statusEmpty": "Empty",
- "statusError": "Error",
- "statusWsdlError": "Error while downloading or parsing of WSDL",
- "allSystemsBtn": "Show all subsystems",
- "statusErrorInfo": "Subsystem either could not be reached by RIA's Monitoring Security Server or there was some other error during the request.",
- "statusEmptyInfo": "Subsystem does not have any methods.",
- "moreMethods": "{{count}} more..."
- }
-}
\ No newline at end of file
+ "statusEmpty": "Empty",
+ "statusError": "Error",
+ "statusWsdlError": "Error while downloading or parsing of WSDL",
+ "statusWsdlTimeout": "WSDL query timed out",
+ "statusWsdlSkipped": "WSDL skipped due to previous Timeout",
+ "allSystemsBtn": "Show all subsystems",
+ "statusErrorInfo": "Subsystem either could not be reached by RIA's Monitoring Security Server or there was some other error during the request.",
+ "statusEmptyInfo": "Subsystem does not have any methods.",
+ "moreMethods": "{{count}} more ...",
+ "incorrectInstanceWarning": "Incorrect instance!",
+ "subsystemNotFoundWarning": "Subsystem \"{{subsystem}}\" cannot be found!"
+ },
+ "scrollToTop": "Scroll to top"
+}
diff --git a/src/assets/i18n/est.json b/src/assets/i18n/est.json
index 2fc97947e452fc1f4b4fb60bdc3a122464ec8882..8dee45fb3d459c864a9152d23dec80d1e1eab692 100644
--- a/src/assets/i18n/est.json
+++ b/src/assets/i18n/est.json
@@ -1,35 +1,47 @@
{
- "index": {
- "title": "X-tee alamsüsteemide ja teenuste kataloog"
+ "index": {
+ "title": "X-tee alamsüsteemide ja teenuste kataloog"
+ },
+ "service": {
+ "dataLoadingError": "Viga andmete laadimisel serverist!"
+ },
+ "subsystemList": {
+ "heading": "X-tee alamsüsteemide kataloog teenuste ja WSDL kirjeldustega",
+ "intro": {
+ "p1": "Antud kataloog on genereeritud saates getWsdl päringud RIA monitooringu turvaserverist kõigi X-tee alamsüsteemide pihta.",
+ "p2": "Alamsüsteemid Tühi ikooniga ei oma X-tee teenuseid.",
+ "p3": "Alamsüsteemid Viga ikooniga kas ei ole ligipääsetavad RIA monitooringu turvaserveri poolt, või päringu tegemisel ilmnes muu viga.",
+ "p4": "Kõige värskem kataloogi versioon JSON kujul: JSON",
+ "p5": "Kasutajatugi: help@ria.ee"
},
- "subsystemList": {
- "heading": "X-tee alamsüsteemide kataloog teenuste ja WSDL kirjeldustega",
- "intro": {
- "p1": "Antud kataloog on genereeritud saates getWsdl päringud RIA monitooringu turvaserverist kõigi X-tee alamsüsteemide pihta.",
- "p2": "Alamsüsteemid Tühi ikooniga ei oma X-tee teenuseid.",
- "p3": "Alamsüsteemid Viga ikooniga kas ei ole ligipääsetavad RIA monitooringu turvaserveri poolt, või päringu tegemisel ilmnes muu viga.",
- "p4": "Kasutajatugi: help@ria.ee"
- },
- "selectInstance": "Vali X-tee instants"
+ "moreSubsystems": "Selleks, et näha rohkem alamsüsteeme, muutke alamsüsteemide näitamise piirangu või otsige alamsüsteemi või teenuse nime järgi.",
+ "selectInstance": "Vali X-tee instants",
+ "selectVersion": "Vali teenuste kataloogi versiooni",
+ "latestVersion": "Viimane versioon"
+ },
+ "search": {
+ "searchFilters": "Otsingu filtrid",
+ "nameFilter": "Filtreeri nime järgi",
+ "limit": "Alamsüsteemide näitamise piirang",
+ "nonEmpty": "Näita vaid teenustepakkujad",
+ "allOption": "Kõik"
+ },
+ "subsystem": {
+ "heading": "Alamsüsteem {{subsystem}} teenuste ja WSDL kirjeldustega X-tee instantsi \"{{instance}}\" jaoks",
+ "intro": {
+ "p1": "Kasutajatugi: help@ria.ee"
},
- "search": {
- "searchFilters": "Otsingu filtrid",
- "nameFilter": "Filtreeri nime järgi",
- "limit": "Alamsüsteemide näitamise piirang",
- "nonEmpty": "Näita vaid teenustepakkujad",
- "allOption": "Kõik"
- },
- "subsystem": {
- "heading": "Alamsüsteem {{subsystem}} teenuste ja WSDL kirjeldustega X-tee instantsi \"{{instance}}\" jaoks",
- "intro": {
- "p1": "Kasutajatugi: help@ria.ee"
- },
- "statusEmpty": "Tühi",
- "statusError": "Viga",
- "statusWsdlError": "Viga WSDL'i laadimisel või töötlemisel",
- "allSystemsBtn": "Näita kõiki alamsüsteeme",
- "statusErrorInfo": "Alamsüsteem kas ei ole ligipääsetav RIA monitooringu turvaserveri poolt, või päringu tegemisel ilmnes muu viga.",
- "statusEmptyInfo": "Alamsüsteem ei oma X-tee teenuseid.",
- "moreMethods": "veel {{count}}..."
- }
+ "statusEmpty": "Tühi",
+ "statusError": "Viga",
+ "statusWsdlError": "Viga WSDL'i laadimisel või töötlemisel",
+ "statusWsdlTimeout": "WSDL'i laadimine aegus",
+ "statusWsdlSkipped": "Eelneva aegumise tõttu WSDL'i laadimine pole teostatud",
+ "allSystemsBtn": "Näita kõiki alamsüsteeme",
+ "statusErrorInfo": "Alamsüsteem kas ei ole ligipääsetav RIA monitooringu turvaserveri poolt, või päringu tegemisel ilmnes muu viga.",
+ "statusEmptyInfo": "Alamsüsteem ei oma X-tee teenuseid.",
+ "moreMethods": "veel {{count}} ...",
+ "incorrectInstanceWarning": "Vale instants!",
+ "subsystemNotFoundWarning": "Alamsüsteem \"{{subsystem}}\" ei ole leitud!"
+ },
+ "scrollToTop": "Lehe algusesse"
}
\ No newline at end of file
diff --git a/src/favicon.ico b/src/favicon.ico
index 59be6177eaadcf1c6d9b2d5fb6c4aae7b64eada2..04acd86c8ef6950acc34651f41e18dff304a533c 100644
Binary files a/src/favicon.ico and b/src/favicon.ico differ
diff --git a/src/favicon_orig.ico b/src/favicon_orig.ico
new file mode 100644
index 0000000000000000000000000000000000000000..59be6177eaadcf1c6d9b2d5fb6c4aae7b64eada2
Binary files /dev/null and b/src/favicon_orig.ico differ
diff --git a/src/karma.conf.js b/src/karma.conf.js
index 85c6c360aa5719877eab63a4ad9d632da52abaef..95bd189470e4b581448d532cf0a65c8295b11da5 100644
--- a/src/karma.conf.js
+++ b/src/karma.conf.js
@@ -3,6 +3,12 @@
module.exports = function (config) {
config.set({
+ customLaunchers: {
+ ChromiumDocker: {
+ base: 'ChromiumHeadless',
+ flags: ['--no-sandbox']
+ }
+ },
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
@@ -16,7 +22,7 @@ module.exports = function (config) {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
- dir: require('path').join(__dirname, '../coverage/methods'),
+ dir: require('path').join(__dirname, '../coverage/xtss-catalogue'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},