aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.browserslistrc7
-rw-r--r--.eslintrc.json30
-rw-r--r--.github/dependabot.yml20
-rw-r--r--.github/workflows/gerrit-merge.yaml117
-rw-r--r--.github/workflows/gerrit-verify.yaml96
-rw-r--r--.gitignore3
-rw-r--r--Dockerfile5
-rw-r--r--README.md41
-rw-r--r--angular.json40
-rw-r--r--docs/_static/logo_onap_2024.pngbin0 -> 11627 bytes
-rw-r--r--docs/conf.py2
-rw-r--r--docs/release-notes.rst41
-rw-r--r--package.json71
-rw-r--r--releases/0.1.2-container-release.yaml7
-rw-r--r--src/app/app-routing.module.ts2
-rw-r--r--src/app/app.module.ts67
-rw-r--r--src/app/guards/auth.guard.ts4
-rw-r--r--src/app/guards/edit-user.can-activate.guard.ts4
-rw-r--r--src/app/guards/has-permissions.guard.ts4
-rw-r--r--src/app/helpers/helpers.ts12
-rw-r--r--src/app/modules/dashboard/apps/user-last-action-tile/user-last-action-tile.component.ts4
-rw-r--r--src/app/modules/i18n/i18n.module.ts2
-rw-r--r--src/app/modules/user-administration/user-administration-form/user-administration-form.component.ts34
-rw-r--r--src/app/services/tileservice/tiles.service.ts14
-rw-r--r--src/app/services/user-settings.service.ts14
-rw-r--r--src/test.ts15
-rw-r--r--tsconfig.json13
-rw-r--r--version.properties2
28 files changed, 476 insertions, 195 deletions
diff --git a/.browserslistrc b/.browserslistrc
deleted file mode 100644
index 16e3010..0000000
--- a/.browserslistrc
+++ /dev/null
@@ -1,7 +0,0 @@
-last 1 Chrome version
-last 1 Firefox version
-last 2 Edge major versions
-last 2 Safari major versions
-last 2 iOS major versions
-Firefox ESR
-not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. \ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
index a4c8c1c..fb48546 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,17 +1,29 @@
{
"root": true,
- "ignorePatterns": ["projects/**/*"],
+ "ignorePatterns": [
+ "projects/**/*"
+ ],
"overrides": [
{
- "files": ["*.ts"],
+ "files": [
+ "*.ts"
+ ],
"parserOptions": {
- "project": ["tsconfig.json", "e2e/tsconfig.json"],
+ "project": [
+ "tsconfig.json",
+ "e2e/tsconfig.json"
+ ],
"createDefaultProgram": true
},
- "extends": ["plugin:@angular-eslint/recommended", "plugin:@angular-eslint/template/process-inline-templates"],
+ "extends": [
+ "plugin:@angular-eslint/recommended",
+ "plugin:@angular-eslint/template/process-inline-templates"
+ ],
"rules": {
"no-unused-vars": "off",
- "@typescript-eslint/no-unused-vars": ["error"],
+ "@typescript-eslint/no-unused-vars": [
+ "error"
+ ],
"@angular-eslint/component-selector": [
"error",
{
@@ -31,8 +43,12 @@
}
},
{
- "files": ["*.html"],
- "extends": ["plugin:@angular-eslint/template/recommended"],
+ "files": [
+ "*.html"
+ ],
+ "extends": [
+ "plugin:@angular-eslint/template/recommended"
+ ],
"rules": {}
}
]
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..8979dd8
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,20 @@
+---
+# Dependabot configured for weekly NPM and Docker scans
+
+version: 2
+updates:
+ # Enable version updates for npm
+ - package-ecosystem: "npm"
+ # Look for `package.json` and `lock` files in the `root` directory
+ directory: "/"
+ # Check the npm registry for updates every day (weekdays)
+ schedule:
+ interval: "weekly"
+
+ # Enable version updates for Docker
+ - package-ecosystem: "docker"
+ # Look for a `Dockerfile` in the `root` directory
+ directory: "/"
+ # Check for updates once a week
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/gerrit-merge.yaml b/.github/workflows/gerrit-merge.yaml
new file mode 100644
index 0000000..3fa0a19
--- /dev/null
+++ b/.github/workflows/gerrit-merge.yaml
@@ -0,0 +1,117 @@
+---
+name: Gerrit Merge
+
+# yamllint disable-line rule:truthy
+on:
+ workflow_dispatch:
+ inputs:
+ GERRIT_BRANCH:
+ description: "Branch that change is against"
+ required: true
+ type: string
+ GERRIT_CHANGE_ID:
+ description: "The ID for the change"
+ required: true
+ type: string
+ GERRIT_CHANGE_NUMBER:
+ description: "The Gerrit number"
+ required: true
+ type: string
+ GERRIT_CHANGE_URL:
+ description: "URL to the change"
+ required: true
+ type: string
+ GERRIT_EVENT_TYPE:
+ description: "Type of Gerrit event"
+ required: true
+ type: string
+ GERRIT_PATCHSET_NUMBER:
+ description: "The patch number for the change"
+ required: true
+ type: string
+ GERRIT_PATCHSET_REVISION:
+ description: "The revision sha"
+ required: true
+ type: string
+ GERRIT_PROJECT:
+ description: "Project in Gerrit"
+ required: true
+ type: string
+ GERRIT_REFSPEC:
+ description: "Gerrit refspec of change"
+ required: true
+ type: string
+ secrets:
+ SONAR_TOKEN:
+ description: "Sonar Cloud access token"
+ required: true
+
+concurrency:
+ # yamllint disable-line rule:line-length
+ group: gerrit-merge-${{ github.workflow }}-${{ github.event.inputs.GERRIT_CHANGE_ID || github.run_id }}
+ cancel-in-progress: true
+
+jobs:
+ notify:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Notify job start
+ # yamllint disable-line rule:line-length
+ uses: lfit/gerrit-review-action@9627b9a144f2a2cad70707ddfae87c87dce60729 # v0.8
+ with:
+ host: ${{ vars.GERRIT_SERVER }}
+ username: ${{ vars.GERRIT_SSH_USER }}
+ key: ${{ secrets.GERRIT_SSH_PRIVKEY }}
+ known_hosts: ${{ vars.GERRIT_KNOWN_HOSTS }}
+ gerrit-change-number: ${{ inputs.GERRIT_CHANGE_NUMBER }}
+ gerrit-patchset-number: ${{ inputs.GERRIT_PATCHSET_NUMBER }}
+ vote-type: clear
+ - name: Allow replication
+ run: sleep 10s
+
+ call-prescan-sonarcloud:
+ needs: notify
+ # yamllint disable-line rule:line-length
+ uses: lfit/releng-reusable-workflows/.github/workflows/composed-generic-sonar-cloud.yaml@main
+ with:
+ GERRIT_BRANCH: ${{ inputs.GERRIT_BRANCH }}
+ GERRIT_CHANGE_ID: ${{ inputs.GERRIT_CHANGE_ID }}
+ GERRIT_CHANGE_NUMBER: ${{ inputs.GERRIT_CHANGE_NUMBER }}
+ GERRIT_CHANGE_URL: ${{ inputs.GERRIT_CHANGE_URL }}
+ GERRIT_EVENT_TYPE: ${{ inputs.GERRIT_EVENT_TYPE }}
+ GERRIT_PATCHSET_NUMBER: ${{ inputs.GERRIT_PATCHSET_NUMBER }}
+ GERRIT_PATCHSET_REVISION: ${{ inputs.GERRIT_PATCHSET_REVISION }}
+ GERRIT_PROJECT: ${{ inputs.GERRIT_PROJECT }}
+ GERRIT_REFSPEC: ${{ inputs.GERRIT_REFSPEC }}
+ JDK_VERSION: "17"
+ ENV_VARS: "{}"
+ ENV_SECRETS: "{}"
+ SONAR_ARGS: >
+ -Dsonar.organization=onap
+ -Dsonar.projectKey=onap_portal-ng-ui
+ -Dsonar.projectName=portal-ng-ui
+ -Dsonar.build.sourceEncoding=UTF-8
+ -Dsonar.sources=.
+ -Dsonar.go.coverage.reportPaths=**/coverage.txt
+ -Dsonar.verbose=true
+ secrets:
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+
+ report-status:
+ if: ${{ always() }}
+ needs: [notify, call-prescan-sonarcloud]
+ runs-on: ubuntu-latest
+ steps:
+ - name: Get workflow conclusion
+ uses: technote-space/workflow-conclusion-action@v3
+ - name: Report workflow conclusion
+ # yamllint disable-line rule:line-length
+ uses: lfit/gerrit-review-action@9627b9a144f2a2cad70707ddfae87c87dce60729 # v0.8
+ with:
+ host: ${{ vars.GERRIT_SERVER }}
+ username: ${{ vars.GERRIT_SSH_USER }}
+ key: ${{ secrets.GERRIT_SSH_PRIVKEY }}
+ known_hosts: ${{ vars.GERRIT_KNOWN_HOSTS }}
+ gerrit-change-number: ${{ inputs.GERRIT_CHANGE_NUMBER }}
+ gerrit-patchset-number: ${{ inputs.GERRIT_PATCHSET_NUMBER }}
+ vote-type: ${{ env.WORKFLOW_CONCLUSION }}
diff --git a/.github/workflows/gerrit-verify.yaml b/.github/workflows/gerrit-verify.yaml
new file mode 100644
index 0000000..7518c60
--- /dev/null
+++ b/.github/workflows/gerrit-verify.yaml
@@ -0,0 +1,96 @@
+---
+name: Gerrit Verify
+
+# yamllint disable-line rule:truthy
+on:
+ workflow_dispatch:
+ inputs:
+ GERRIT_BRANCH:
+ description: 'Branch that change is against'
+ required: true
+ type: string
+ GERRIT_CHANGE_ID:
+ description: 'The ID for the change'
+ required: true
+ type: string
+ GERRIT_CHANGE_NUMBER:
+ description: 'The Gerrit number'
+ required: true
+ type: string
+ GERRIT_CHANGE_URL:
+ description: 'URL to the change'
+ required: true
+ type: string
+ GERRIT_EVENT_TYPE:
+ description: 'Type of Gerrit event'
+ required: true
+ type: string
+ GERRIT_PATCHSET_NUMBER:
+ description: 'The patch number for the change'
+ required: true
+ type: string
+ GERRIT_PATCHSET_REVISION:
+ description: 'The revision sha'
+ required: true
+ type: string
+ GERRIT_PROJECT:
+ description: 'Project in Gerrit'
+ required: true
+ type: string
+ GERRIT_REFSPEC:
+ description: 'Gerrit refspec of change'
+ required: true
+ type: string
+
+concurrency:
+ # yamllint disable-line rule:line-length
+ group: gerrit-merge-${{ github.workflow }}-${{ github.event.inputs.GERRIT_CHANGE_ID || github.run_id }}
+ cancel-in-progress: true
+
+jobs:
+ notify:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Notify job start
+ # yamllint disable-line rule:line-length
+ uses: lfit/gerrit-review-action@9627b9a144f2a2cad70707ddfae87c87dce60729 # v0.8
+ with:
+ host: ${{ vars.GERRIT_SERVER }}
+ username: ${{ vars.GERRIT_SSH_USER }}
+ key: ${{ secrets.GERRIT_SSH_PRIVKEY }}
+ known_hosts: ${{ vars.GERRIT_KNOWN_HOSTS }}
+ gerrit-change-number: ${{ inputs.GERRIT_CHANGE_NUMBER }}
+ gerrit-patchset-number: ${{ inputs.GERRIT_PATCHSET_NUMBER }}
+ vote-type: clear
+ - name: Allow replication
+ run: sleep 10s
+
+ # This is a test workflow, not production, and will be replaced
+ node-sonartype-lifecycle:
+ needs: [notify]
+ # yamllint disable-line rule:line-length
+ uses: ModeSevenIndustrialSolutions/portal-ng-ui/.github/workflows/node-sonatype-lifecycle.yaml@master
+ with:
+ node-version: 20
+ build-tool: npm
+ secrets:
+ NEXUS_IQ_PASSWORD: ${{ secrets.NEXUS_IQ_PASSWORD }}
+
+ report-status:
+ if: ${{ always() }}
+ needs: [notify, node-sonartype-lifecycle]
+ runs-on: ubuntu-latest
+ steps:
+ - name: Get workflow conclusion
+ uses: technote-space/workflow-conclusion-action@v3
+ - name: Report workflow conclusion
+ # yamllint disable-line rule:line-length
+ uses: lfit/gerrit-review-action@9627b9a144f2a2cad70707ddfae87c87dce60729 # v0.8
+ with:
+ host: ${{ vars.GERRIT_SERVER }}
+ username: ${{ vars.GERRIT_SSH_USER }}
+ key: ${{ secrets.GERRIT_SSH_PRIVKEY }}
+ known_hosts: ${{ vars.GERRIT_KNOWN_HOSTS }}
+ gerrit-change-number: ${{ inputs.GERRIT_CHANGE_NUMBER }}
+ gerrit-patchset-number: ${{ inputs.GERRIT_PATCHSET_NUMBER }}
+ vote-type: ${{ env.WORKFLOW_CONCLUSION }}
diff --git a/.gitignore b/.gitignore
index 200a708..531587f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@
# dependencies
/node_modules
package-lock.json
+node
# profiling files
chrome-profiler-events*.json
@@ -56,4 +57,4 @@ Thumbs.db
/.angular
.cache
-staging.proxy.config.json \ No newline at end of file
+staging.proxy.config.json
diff --git a/Dockerfile b/Dockerfile
index 31cc8cb..4eb1cbb 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,8 @@
# the JRE is required by the openapi-generator-cli
-FROM eclipse-temurin:17-jre-alpine as builder
+# FROM eclipse-temurin:17-jre-alpine as builder
+FROM node:18-alpine AS builder
RUN apk update && \
- apk add nodejs npm
+ apk add openjdk17
WORKDIR /usr/src/app
COPY package*.json ./
diff --git a/README.md b/README.md
index 0e133c9..0d49135 100644
--- a/README.md
+++ b/README.md
@@ -3,13 +3,24 @@
## Getting started
The portal-ng ui can either be developed against a remote cluster or it's dependencies can be run locally on your machine.
+### Allow unpriviliged (non-sudo) programs to bind to low ports (i.e 80)
+
+On linux, programs that run in an unprivileged mode are not allowed to bind to low ports (<=1024).
+A capability needs to be defined to allow the `node` process to do so:
+
+```sh
+sudo setcap 'cap_net_bind_service=+ep' `readlink -f \`which node\``
+```
+
### Developing against a remote cluster
To develop against a remote cluster, the webpack proxy needs to be configured to forward requests to the remote cluster urls.
For that we are providing a `staging.proxy.config.json.template` file that needs to be adjusted with your cluster hostnames and then saved as `staging.proxy.config.json`.
After that, you can either use `npm start` or the `staging.sh` to launch the portal-ng in development mode:
+
```sh
./staging.sh
```
+
```sh
npm start
```
@@ -18,13 +29,14 @@ npm start
We are providing a docker-compose file that can be used to spin up the portal-ng and it's dependencies (like Keycloak or the bff) on your machine.
To do that, execute the `run.sh` in the development folder:
-```shell
+
+```sh
development/run.sh
```
To stop the portal-ng, portal backend services, Keycloak and the databases run:
-```shell
+```sh
development/stop.sh
```
@@ -35,21 +47,24 @@ You can access the portal-ng UI via browser with different default user accounts
in what they are allowed to see in the portal.
URL: http://localhost
-```
-username: onap-admin
+
+``` yaml
+username: onap-admin
password: password
-username: onap-designer
+username: onap-designer
password: password
-username: onap-operator
+username: onap-operator
password: password
```
+
You can access the Keycloak UI via browser.
URL: http://localhost:8080
-```
-username: admin
+
+``` yaml
+username: admin
password: password
```
@@ -60,7 +75,7 @@ Run `npm run build -- --prod --base-href=/portal-ui/` to get a production build
In the configuration of nginx (the `nginx.template`) we have a few environment variables that need to be set.
-```bash
+```sh
export NGINX_PORT=80
export BFF_URL=http://bff:9080/
export WIREMOCK_URL=http://wiremock:8080/
@@ -68,14 +83,16 @@ export WIREMOCK_URL=http://wiremock:8080/
Finally, build the image with
-```bash
+```sh
docker build -t portal-ng .
```
### Run the docker image
-```bash
+```sh
docker run -e "NGINX_PORT=80" -e "BFF_URL=http:bff:9080/" -e "WIREMOCK_URL=http://wiremock:8080/" -p 8080:80 portal-ng
```
-Note that this will not work on its own, because the referenced containers (`BFF` and `WIREMOCK`) are most likely not available in your local environment. You would have to run them as well, or pass in other urls (like `example.com`) to get the container running locally. Obviously this does not get you very far though.
+Note that this will not work on its own, because the referenced containers (`BFF` and `WIREMOCK`) are most likely not available in your local environment.
+You would have to run them as well, or pass in other urls (like `example.com`) to get the container running locally.
+Obviously this does not get you very far though.
diff --git a/angular.json b/angular.json
index f749b51..339a24b 100644
--- a/angular.json
+++ b/angular.json
@@ -13,19 +13,25 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
- "allowedCommonJsDependencies": [ "clone-deep"],
+ "allowedCommonJsDependencies": [
+ "clone-deep"
+ ],
"aot": true,
"outputPath": "dist/frontend",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
- "assets": ["src/favicon.ico", "src/assets", "src/keycloak-error.html"],
+ "assets": [
+ "src/favicon.ico",
+ "src/assets",
+ "src/keycloak-error.html"
+ ],
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.css",
"src/assets/css/bootstrap-icons.css",
"src/styles.css",
- "./node_modules/@angular/cdk/overlay-prebuilt.css",
+ "./node_modules/@angular/cdk/overlay-prebuilt.css",
{
"input": "src/assets/css/onap-styles.css",
"bundleName": "onap-styles",
@@ -96,15 +102,24 @@
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
- "assets": ["src/favicon.ico", "src/assets", "src/keycloak-error.html"],
- "styles": ["src/styles.css"],
+ "assets": [
+ "src/favicon.ico",
+ "src/assets",
+ "src/keycloak-error.html"
+ ],
+ "styles": [
+ "src/styles.css"
+ ],
"scripts": []
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
- "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
+ "lintFilePatterns": [
+ "src/**/*.ts",
+ "src/**/*.html"
+ ]
}
},
"e2e": {
@@ -122,7 +137,6 @@
}
}
},
- "defaultProject": "frontend",
"cli": {
"cache": {
"enabled": true,
@@ -130,6 +144,16 @@
"environment": "all"
},
"analytics": "8f37c777-0688-43ed-a5a5-b315e820eff9",
- "defaultCollection": "@angular-eslint/schematics"
+ "schematicCollections": [
+ "@angular-eslint/schematics"
+ ]
+ },
+ "schematics": {
+ "@angular-eslint/schematics:application": {
+ "setParserOptionsProject": true
+ },
+ "@angular-eslint/schematics:library": {
+ "setParserOptionsProject": true
+ }
}
}
diff --git a/docs/_static/logo_onap_2024.png b/docs/_static/logo_onap_2024.png
new file mode 100644
index 0000000..55d307f
--- /dev/null
+++ b/docs/_static/logo_onap_2024.png
Binary files differ
diff --git a/docs/conf.py b/docs/conf.py
index 8cdff9b..e0df9e1 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -11,7 +11,7 @@ html_theme = "sphinx_rtd_theme"
html_theme_options = {
"style_nav_header_background": "white",
"sticky_navigation": "False" }
-html_logo = "_static/logo_onap_2017.png"
+html_logo = "_static/logo_onap_2024.png"
html_favicon = "_static/favicon.ico"
html_static_path = ["_static"]
html_show_sphinx = False
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
index b876645..dcc0199 100644
--- a/docs/release-notes.rst
+++ b/docs/release-notes.rst
@@ -5,7 +5,7 @@
.. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING
.. _release_notes:
-Portal-ng Release Notes
+Portal-NG Release Notes
#######################
.. contents::
@@ -22,29 +22,30 @@ Version: 0.1.0
Release Data
------------
-+--------------------------------------+--------------------------------------------------------+
-| **Portal-ng Project** | |
-| | |
-+--------------------------------------+--------------------------------------------------------+
-| **Docker images** | onap/portal-ng/ui:0.1.1 |
-| | onap/portal-ng/bff:0.1.0 |
-| | onap/portal-ng/preferences:0.1.1 |
-| | onap/portal-ng/history:0.1.1 |
-| | |
-+--------------------------------------+--------------------------------------------------------+
-| **Release designation** | 0.1.1 New Delhi |
-| | |
-+--------------------------------------+--------------------------------------------------------+
-| **Release date** | 2024 June 13 |
-| | |
-+--------------------------------------+--------------------------------------------------------+
++------------------------------+----------------------------------------------+
+| **Portal-ng Project** | |
+| | |
++------------------------------+----------------------------------------------+
+| **Docker images** | onap/portal-ng/ui:0.1.1 |
+| | onap/portal-ng/bff:0.1.0 |
+| | onap/portal-ng/preferences:0.1.1 |
+| | onap/portal-ng/history:0.1.1 |
+| | |
++------------------------------+----------------------------------------------+
+| **Release designation** | 0.1.1 New Delhi |
+| | |
++------------------------------+----------------------------------------------+
+| **Release date** | 2024 June 13 |
+| | |
++------------------------------+----------------------------------------------+
Features
--------
-The Portal-NG is the successor of the Portal project, which has been discontinued. It offers a dashboard with the last user-actions in the portal,
-an app-starter that references the other ui's available in ONAP and
-a user management that allows creating and editing users and their roles.
+The Portal-NG is the successor of the Portal project, which has been
+discontinued. It offers a dashboard with the last user-actions in the portal,
+an app-starter that references the other ui's available in ONAP and a user
+management that allows creating and editing users and their roles.
.. _newdelhi_deliverable:
diff --git a/package.json b/package.json
index cbd5fd0..c6d404e 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"openapi": "npx openapi-generator-cli generate --generator-key v3.0",
"ng": "./node-modules/.bin/ng",
"prestart": "npm run openapi",
- "start": "ng serve -- --proxy-config staging.proxy.config.json --host 0.0.0.0 --port 80 --disable-host-check",
+ "start": "ng serve --proxy-config staging.proxy.config.json --host 0.0.0.0 --port 80 --disable-host-check",
"prebuild": "npm run openapi",
"build": "ng build",
"pretest": "npm run openapi",
@@ -21,47 +21,46 @@
},
"private": true,
"dependencies": {
- "@angular/animations": "13.3.10",
- "@angular/cdk": "13.3.8",
- "@angular/common": "13.3.10",
- "@angular/compiler": "13.3.10",
- "@angular/core": "13.3.10",
- "@angular/forms": "13.3.10",
- "@angular/localize": "13.3.10",
- "@angular/platform-browser": "13.3.10",
- "@angular/platform-browser-dynamic": "13.3.10",
- "@angular/router": "13.3.10",
- "@ng-bootstrap/ng-bootstrap": "12.1.2",
- "@ngx-translate/core": "13.0.0",
- "@ngx-translate/http-loader": "6.0.0",
- "@popperjs/core": "2.11.5",
- "angular-oauth2-oidc": "12.1.0",
- "bootstrap": "5.1.3",
+ "@angular/animations": "16.2.12",
+ "@angular/cdk": "16.2.14",
+ "@angular/common": "16.2.12",
+ "@angular/compiler": "16.2.12",
+ "@angular/core": "16.2.12",
+ "@angular/forms": "16.2.12",
+ "@angular/localize": "16.2.12",
+ "@angular/platform-browser": "16.2.12",
+ "@angular/platform-browser-dynamic": "16.2.12",
+ "@angular/router": "16.2.12",
+ "@ng-bootstrap/ng-bootstrap": "15.1.2",
+ "@ngx-translate/core": "14.0.0",
+ "@ngx-translate/http-loader": "7.0.0",
+ "@popperjs/core": "^2.11.6",
+ "angular-oauth2-oidc": "^16.0.0",
+ "bootstrap": "5.2.3",
"license-report": "5.0.2",
"lodash": "4.17.21",
- "ng-bootstrap": "1.6.3",
- "rxjs": "6.6.7",
- "tslib": "2.2.0",
- "zone.js": "0.11.5"
+ "rxjs": "~7.4.0",
+ "tslib": "2.3.0",
+ "zone.js": "0.13.3"
},
"devDependencies": {
- "@angular-devkit/build-angular": "13.3.7",
- "@angular-eslint/builder": "13.2.1",
- "@angular-eslint/eslint-plugin": "13.2.1",
- "@angular-eslint/eslint-plugin-template": "13.2.1",
- "@angular-eslint/schematics": "13.2.1",
- "@angular-eslint/template-parser": "13.2.1",
- "@angular/cli": "13.3.7",
- "@angular/compiler-cli": "13.3.10",
+ "@angular-devkit/build-angular": "16.2.16",
+ "@angular-eslint/builder": "16.3.1",
+ "@angular-eslint/eslint-plugin": "16.3.1",
+ "@angular-eslint/eslint-plugin-template": "16.3.1",
+ "@angular-eslint/schematics": "16.3.1",
+ "@angular-eslint/template-parser": "16.3.1",
+ "@angular/cli": "16.2.16",
+ "@angular/compiler-cli": "16.2.12",
"@openapitools/openapi-generator-cli": "^2.6.0",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "2.0.3",
- "@types/lodash": "^4.14.188",
- "@types/node": "16.0.0",
+ "@types/lodash": "4.14.188",
+ "@types/node": "18.0.0",
"@types/uuid": "^8.3.4",
- "@typescript-eslint/eslint-plugin": "5.17.0",
- "@typescript-eslint/parser": "5.17.0",
- "eslint": "7.32.0",
+ "@typescript-eslint/eslint-plugin": "^5.59.2",
+ "@typescript-eslint/parser": "^5.59.2",
+ "eslint": "^8.39.0",
"git-format-staged": "3.0.0",
"husky": "8.0.1",
"jasmine-core": "~4.0.0",
@@ -81,8 +80,8 @@
"prettier": "2.6.2",
"protractor": "7.0.0",
"sonar-scanner": "3.1.0",
- "ts-node": "10.0.0",
- "typescript": "4.6.4"
+ "ts-node": "10.9.0",
+ "typescript": "~4.9.3"
},
"husky": {
"hooks": {
diff --git a/releases/0.1.2-container-release.yaml b/releases/0.1.2-container-release.yaml
new file mode 100644
index 0000000..c3c426d
--- /dev/null
+++ b/releases/0.1.2-container-release.yaml
@@ -0,0 +1,7 @@
+distribution_type: container
+container_release_tag: 0.1.2
+project: portal-ng/ui
+ref: 49260befded7829a951168faa36f7dcad9402823
+containers:
+ - name: portal-ng/ui
+ version: latest
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 0f5a50e..d4e4d7c 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -49,7 +49,7 @@ const routes: Routes = [
];
@NgModule({
- imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy', onSameUrlNavigation: 'reload' })],
+ imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' })],
exports: [RouterModule],
})
export class AppRoutingModule {}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index faeb566..854c8fc 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -65,39 +65,38 @@ export function changeConfig() {
}
@NgModule({
- declarations: [
- AppComponent,
- HeaderComponent,
- ModalContentComponent,
- SidemenuComponent,
- PageNotFoundComponent,
- ConfirmationModalComponent,
- StatusPageComponent,
- ],
- imports: [
- BrowserModule,
- AppRoutingModule,
- HttpClientModule,
- AuthConfigModule,
- AlertModule,
- AuthConfigModule,
- ApiModule.forRoot(changeConfig),
- OAuthModule.forRoot({ resourceServer: { sendAccessToken: false } }),
- BrowserAnimationsModule,
- UserAdministrationModule,
- DashboardModule,
- SharedModule,
- AppStarterModule,
- ],
- // { provide: ErrorHandler, useClass: SentryErrorHandler },
- providers: [
- { provide: RequestCache, useClass: RequestCacheService },
- httpInterceptorProviders,
- { provide: OAuthStorage, useValue: localStorage },
- LoadingIndicatorService,
- { provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy },
- ],
- bootstrap: [AppComponent],
- entryComponents: [ModalContentComponent, ConfirmationModalComponent],
+ declarations: [
+ AppComponent,
+ HeaderComponent,
+ ModalContentComponent,
+ SidemenuComponent,
+ PageNotFoundComponent,
+ ConfirmationModalComponent,
+ StatusPageComponent,
+ ],
+ imports: [
+ BrowserModule,
+ AppRoutingModule,
+ HttpClientModule,
+ AuthConfigModule,
+ AlertModule,
+ AuthConfigModule,
+ ApiModule.forRoot(changeConfig),
+ OAuthModule.forRoot({ resourceServer: { sendAccessToken: false } }),
+ BrowserAnimationsModule,
+ UserAdministrationModule,
+ DashboardModule,
+ SharedModule,
+ AppStarterModule,
+ ],
+ // { provide: ErrorHandler, useClass: SentryErrorHandler },
+ providers: [
+ { provide: RequestCache, useClass: RequestCacheService },
+ httpInterceptorProviders,
+ { provide: OAuthStorage, useValue: localStorage },
+ LoadingIndicatorService,
+ { provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy },
+ ],
+ bootstrap: [AppComponent]
})
export class AppModule {}
diff --git a/src/app/guards/auth.guard.ts b/src/app/guards/auth.guard.ts
index 54ede0f..645819c 100644
--- a/src/app/guards/auth.guard.ts
+++ b/src/app/guards/auth.guard.ts
@@ -18,7 +18,7 @@
import { Injectable } from '@angular/core';
-import { CanActivate, UrlTree } from '@angular/router';
+import { UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from '../services/auth.service';
@@ -28,7 +28,7 @@ import { AuthService } from '../services/auth.service';
@Injectable({
providedIn: 'root',
})
-export class AuthGuard implements CanActivate {
+export class AuthGuard {
roles: string = '';
constructor(private authService: AuthService) {}
diff --git a/src/app/guards/edit-user.can-activate.guard.ts b/src/app/guards/edit-user.can-activate.guard.ts
index 81fc36e..29c927a 100644
--- a/src/app/guards/edit-user.can-activate.guard.ts
+++ b/src/app/guards/edit-user.can-activate.guard.ts
@@ -18,7 +18,7 @@
import { Injectable } from '@angular/core';
-import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
+import { ActivatedRouteSnapshot, Router } from '@angular/router';
import { Observable, of } from 'rxjs';
import { UsersService } from '../../../openapi/output';
import { catchError, map } from 'rxjs/operators';
@@ -26,7 +26,7 @@ import { TranslateService } from '@ngx-translate/core';
@Injectable({
providedIn: 'root',
})
-export class EditUserCanActivateGuard implements CanActivate {
+export class EditUserCanActivateGuard {
constructor(private usersService: UsersService, private router: Router, private translateService: TranslateService) {}
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
const userId = route.paramMap.get('userId');
diff --git a/src/app/guards/has-permissions.guard.ts b/src/app/guards/has-permissions.guard.ts
index cc04673..1731a33 100644
--- a/src/app/guards/has-permissions.guard.ts
+++ b/src/app/guards/has-permissions.guard.ts
@@ -18,7 +18,7 @@
import { Inject, Injectable } from '@angular/core';
-import { ActivatedRouteSnapshot, CanActivate, Router, UrlTree } from '@angular/router';
+import { ActivatedRouteSnapshot, Router, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { ACL_CONFIG, AclConfig } from '../modules/auth/injection-tokens';
@@ -29,7 +29,7 @@ import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
-export class HasPermissionsGuard implements CanActivate {
+export class HasPermissionsGuard {
constructor(
private readonly authService: AuthService,
private readonly httpClient: HttpClient,
diff --git a/src/app/helpers/helpers.ts b/src/app/helpers/helpers.ts
index 7c03dbd..6b0c486 100644
--- a/src/app/helpers/helpers.ts
+++ b/src/app/helpers/helpers.ts
@@ -17,7 +17,7 @@
*/
-import { FormArray, FormGroup } from '@angular/forms';
+import { UntypedFormArray, UntypedFormGroup } from '@angular/forms';
export function isNotUndefined<T>(val: T | undefined): val is T {
return val !== undefined;
@@ -27,7 +27,7 @@ export function isNotNull<T>(val: T | null): val is T {
return val !== null;
}
-export function markAsDirtyAndValidate(formGroup: FormGroup): void {
+export function markAsDirtyAndValidate(formGroup: UntypedFormGroup): void {
Object.values(formGroup.controls).forEach(control => {
control.markAsDirty();
control.updateValueAndValidity();
@@ -54,10 +54,10 @@ export function getRandomNumber(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
-export function areFormControlsValid(form: FormGroup): boolean {
+export function areFormControlsValid(form: UntypedFormGroup): boolean {
const formControls = Object.keys(form.controls)
.map(key => form.controls[key])
- .filter(control => !(control instanceof FormArray));
+ .filter(control => !(control instanceof UntypedFormArray));
return formControls.find(control => control.invalid && (control.dirty || control.touched)) === undefined;
}
@@ -68,9 +68,9 @@ export function isString(value: any): boolean {
export function resetSelectDefaultValue(cssSelector: string): void {
setTimeout(() => {
const element = document.querySelector(cssSelector);
- if (element) {
+ if (element && document.querySelector(cssSelector)) {
//@ts-ignore
- document.querySelector(cssSelector)?.selectedIndex = -1;
+ document.querySelector(cssSelector).selectedIndex = -1;
}
}, 0);
}
diff --git a/src/app/modules/dashboard/apps/user-last-action-tile/user-last-action-tile.component.ts b/src/app/modules/dashboard/apps/user-last-action-tile/user-last-action-tile.component.ts
index c03016f..ee477b5 100644
--- a/src/app/modules/dashboard/apps/user-last-action-tile/user-last-action-tile.component.ts
+++ b/src/app/modules/dashboard/apps/user-last-action-tile/user-last-action-tile.component.ts
@@ -61,12 +61,12 @@ export class UserLastActionTileComponent implements OnInit {
.pipe(shareReplay({ refCount: true, bufferSize: 1 }));
public actionFilterType$ = this.userActionsSettings$.pipe(
- selectDistinctState<LastUserActionSettings, ActionFilter>(STATE_KEYS.FILTER_TYPE),
+ selectDistinctState<LastUserActionSettings, ActionFilter>(STATE_KEYS.FILTER_TYPE as keyof LastUserActionSettings),
shareReplay({ refCount: true, bufferSize: 1 }),
);
public actionIntervalType$ = this.userActionsSettings$.pipe(
- selectDistinctState<LastUserActionSettings, ActionInterval>(STATE_KEYS.INTERVAL),
+ selectDistinctState<LastUserActionSettings, ActionInterval>(STATE_KEYS.INTERVAL as keyof LastUserActionSettings),
shareReplay({ refCount: true, bufferSize: 1 }),
);
diff --git a/src/app/modules/i18n/i18n.module.ts b/src/app/modules/i18n/i18n.module.ts
index 52bedbe..a6a1da6 100644
--- a/src/app/modules/i18n/i18n.module.ts
+++ b/src/app/modules/i18n/i18n.module.ts
@@ -41,7 +41,7 @@ import { TranslateHttpLoader } from '@ngx-translate/http-loader';
export class I18nModule {
constructor(translate: TranslateService) {
translate.addLangs(['en', 'de']);
- const browserLang = translate.getBrowserLang();
+ const browserLang = translate.getBrowserLang() ?? 'en';
translate.use(browserLang.match(/en|de/) ? browserLang : 'en');
}
}
diff --git a/src/app/modules/user-administration/user-administration-form/user-administration-form.component.ts b/src/app/modules/user-administration/user-administration-form/user-administration-form.component.ts
index 7df2700..2c93654 100644
--- a/src/app/modules/user-administration/user-administration-form/user-administration-form.component.ts
+++ b/src/app/modules/user-administration/user-administration-form/user-administration-form.component.ts
@@ -18,7 +18,7 @@
import { Component, OnInit } from '@angular/core';
-import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms';
+import { AbstractControl, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import {
CreateUserRequest,
Role,
@@ -47,7 +47,7 @@ import { HistoryService } from '../../../services/history.service';
})
export class UserAdministrationFormComponent implements OnInit {
public readonly userId: string | null;
- public readonly keycloakUserForm: FormGroup;
+ public readonly keycloakUserForm: UntypedFormGroup;
public user: UserResponse | undefined = undefined;
public checkBoxes: {
@@ -70,17 +70,17 @@ export class UserAdministrationFormComponent implements OnInit {
) {
this.userId = this.route.snapshot.paramMap.get('userId');
- this.keycloakUserForm = new FormGroup({
- id: new FormControl({ value: null, disabled: true }),
- username: new FormControl({ value: null, disabled: this.userId !== null }, [
+ this.keycloakUserForm = new UntypedFormGroup({
+ id: new UntypedFormControl({ value: null, disabled: true }),
+ username: new UntypedFormControl({ value: null, disabled: this.userId !== null }, [
Validators.required,
Validators.maxLength(50),
Validators.pattern(VALIDATION_PATTERN),
Validators.pattern(NON_WHITE_SPACE_PATTERN),
]),
- email: new FormControl(null, [Validators.email, Validators.required, Validators.pattern(VALIDATION_PATTERN)]),
- firstName: new FormControl(null, [Validators.pattern(VALIDATION_PATTERN)]),
- lastName: new FormControl(null, [Validators.pattern(VALIDATION_PATTERN)]),
+ email: new UntypedFormControl(null, [Validators.email, Validators.required, Validators.pattern(VALIDATION_PATTERN)]),
+ firstName: new UntypedFormControl(null, [Validators.pattern(VALIDATION_PATTERN)]),
+ lastName: new UntypedFormControl(null, [Validators.pattern(VALIDATION_PATTERN)]),
});
}
@@ -121,20 +121,20 @@ export class UserAdministrationFormComponent implements OnInit {
}
}
- get userName(): FormControl {
- return this.keycloakUserForm.get('username') as FormControl;
+ get userName(): UntypedFormControl {
+ return this.keycloakUserForm.get('username') as UntypedFormControl;
}
- get email(): FormControl {
- return this.keycloakUserForm.get('email') as FormControl;
+ get email(): UntypedFormControl {
+ return this.keycloakUserForm.get('email') as UntypedFormControl;
}
- get firstName(): FormControl {
- return this.keycloakUserForm.get('firstName') as FormControl;
+ get firstName(): UntypedFormControl {
+ return this.keycloakUserForm.get('firstName') as UntypedFormControl;
}
- get lastName(): FormControl {
- return this.keycloakUserForm.get('lastName') as FormControl;
+ get lastName(): UntypedFormControl {
+ return this.keycloakUserForm.get('lastName') as UntypedFormControl;
}
public onSubmit(): void {
@@ -212,7 +212,7 @@ export class UserAdministrationFormComponent implements OnInit {
private updateUserData(userResponse: Observable<UserResponse>, roleResponse: Observable<RoleListResponse>): void {
forkJoin([userResponse, roleResponse])
.pipe(
- switchMap(([,]) =>
+ switchMap(() =>
this.historyService.createUserHistoryAction({
type: ActionType.EDIT,
entity: EntityType.USERADMINISTRATION,
diff --git a/src/app/services/tileservice/tiles.service.ts b/src/app/services/tileservice/tiles.service.ts
index 167e42a..acdce5b 100644
--- a/src/app/services/tileservice/tiles.service.ts
+++ b/src/app/services/tileservice/tiles.service.ts
@@ -19,7 +19,7 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
-import { Observable } from 'rxjs';
+import { firstValueFrom, Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { Tile, TilesListResponse } from '../../model/tile';
import { map } from 'rxjs/operators';
@@ -50,8 +50,8 @@ export class TilesService {
* GET tile by id
* @param id to get specific tile
*/
- getTileById(id: number): Promise<Tile> {
- return this.httpClient.get<Tile>(urlTileApi + '/' + id).toPromise();
+ getTileById(id: number): Promise<Tile | undefined> {
+ return firstValueFrom(this.httpClient.get<Tile>(urlTileApi + '/' + id));
}
/**
@@ -59,11 +59,11 @@ export class TilesService {
* @param tile
* @returns the new saved tile
*/
- saveTiles(tile: Tile): Promise<Tile> {
+ saveTiles(tile: Tile): Promise<Tile | undefined> {
const options = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
};
- return this.httpClient.post<Tile>(urlTileApi, tile, options).toPromise();
+ return firstValueFrom(this.httpClient.post<Tile>(urlTileApi, tile, options));
}
/**
@@ -71,11 +71,11 @@ export class TilesService {
* @returns the updated hero
* @param tile
*/
- updateTiles(tile: Tile): Promise<Tile> {
+ updateTiles(tile: Tile): Promise<Tile | undefined> {
const options = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
};
- return this.httpClient.put<Tile>(urlTileApi + '/' + tile.id, tile, options).toPromise();
+ return firstValueFrom(this.httpClient.put<Tile>(urlTileApi + '/' + tile.id, tile, options));
}
/**
diff --git a/src/app/services/user-settings.service.ts b/src/app/services/user-settings.service.ts
index cbaa992..2f7e578 100644
--- a/src/app/services/user-settings.service.ts
+++ b/src/app/services/user-settings.service.ts
@@ -49,14 +49,14 @@ export class UserSettingsService {
}
selectDashboard = () =>
- this.getPreferences$().pipe(selectDistinctState<UserPreferencesModel, DashboardModel>(STATE_KEYS.DASHBOARD));
+ this.getPreferences$().pipe(selectDistinctState<UserPreferencesModel, DashboardModel>(STATE_KEYS.DASHBOARD as keyof UserPreferencesModel));
selectDashboardApps = () =>
- this.selectDashboard().pipe(selectDistinctState<DashboardModel, DashboardAppsModel>(STATE_KEYS.APPS));
+ this.selectDashboard().pipe(selectDistinctState<DashboardModel, DashboardAppsModel>(STATE_KEYS.APPS as keyof DashboardModel));
selectDashboardAvailableTiles = () =>
- this.selectDashboardApps().pipe(selectDistinctState<DashboardAppsModel, DashboardTileSettings[]>(STATE_KEYS.TILES));
+ this.selectDashboardApps().pipe(selectDistinctState<DashboardAppsModel, DashboardTileSettings[]>(STATE_KEYS.TILES as keyof DashboardAppsModel));
selectLastUserAction = () =>
this.selectDashboardApps().pipe(
- selectDistinctState<DashboardAppsModel, LastUserActionSettings>(STATE_KEYS.USER_ACTIONS),
+ selectDistinctState<DashboardAppsModel, LastUserActionSettings>(STATE_KEYS.USER_ACTIONS as keyof DashboardAppsModel),
);
getPreferences(): void {
@@ -106,6 +106,8 @@ export class UserSettingsService {
}
}
-export function selectDistinctState<T, I>(key: string): UnaryFunction<Observable<T>, Observable<I>> {
- return pipe(pluck<T, I>(key), distinctUntilChanged<I>());
+export function selectDistinctState<T, I>(key: keyof T): UnaryFunction<Observable<T>, Observable<I>> {
+ // return pipe(map(x => x[key] as I), distinctUntilChanged<I>());
+ return pipe(pluck(key), map(value => value as unknown as I), distinctUntilChanged());
+ // return pipe(pluck<T, I>(key), distinctUntilChanged<I>());
}
diff --git a/src/test.ts b/src/test.ts
index 36b9be6..c64afa3 100644
--- a/src/test.ts
+++ b/src/test.ts
@@ -23,20 +23,5 @@ import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
-declare const require: {
- context(
- path: string,
- deep?: boolean,
- filter?: RegExp,
- ): {
- keys(): string[];
- <T>(id: string): T;
- };
-};
-
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
-// Then we find all the tests.
-const context = require.context('./', true, /\.spec\.ts$/);
-// And load the modules.
-context.keys().forEach(context);
diff --git a/tsconfig.json b/tsconfig.json
index 536748b..9cdbb46 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -14,15 +14,18 @@
"module": "es2020",
"moduleResolution": "node",
"importHelpers": true,
- "target": "es2017",
- "lib": ["es2018", "dom"],
+ "target": "ES2022",
+ "lib": [
+ "es2020",
+ "dom"
+ ],
"resolveJsonModule": true,
- "esModuleInterop": true
+ "esModuleInterop": true,
+ "useDefineForClassFields": false
},
"angularCompilerOptions": {
"strictInjectionParameters": true,
"strictTemplates": true,
- "fullTemplateTypeCheck": true,
- "enableIvy": true
+ "fullTemplateTypeCheck": true
}
}
diff --git a/version.properties b/version.properties
index b179fec..30ce569 100644
--- a/version.properties
+++ b/version.properties
@@ -4,7 +4,7 @@
major=0
minor=1
-patch=1
+patch=2
base_version=${major}.${minor}.${patch}