mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-16 18:13:06 +00:00
Compare commits
37
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72c4395309 | ||
|
|
09c35ea903 | ||
|
|
923feccdb9 | ||
|
|
e7024d1970 | ||
|
|
a72726427b | ||
|
|
054ecf3dcb | ||
|
|
8c51e783e5 | ||
|
|
fd88e1014c | ||
|
|
2ec227c331 | ||
|
|
2235c6b09e | ||
|
|
dfa6db447f | ||
|
|
e7bba32679 | ||
|
|
ea81fcd6ad | ||
|
|
f1fea0a70b | ||
|
|
0564e67ba6 | ||
|
|
3ac2a91ea0 | ||
|
|
88b857a8fe | ||
|
|
51c55674b4 | ||
|
|
93c872b65d | ||
|
|
195194fb15 | ||
|
|
49375181e4 | ||
|
|
aae4efdd92 | ||
|
|
a4e62974ea | ||
|
|
81fed833f5 | ||
|
|
609144551c | ||
|
|
5bc4c02a94 | ||
|
|
04d488b8ea | ||
|
|
dabab63f78 | ||
|
|
b277a29f32 | ||
|
|
bdeb8a8e51 | ||
|
|
9c0313a00c | ||
|
|
2200135b82 | ||
|
|
a141d19629 | ||
|
|
15ed9a5f81 | ||
|
|
f205cbc33c | ||
|
|
e0f0486e66 | ||
|
|
0576aac985 |
@@ -23,4 +23,6 @@ runs:
|
||||
echo "TAG_NPM not set, aborting"
|
||||
exit 1
|
||||
fi
|
||||
./scripts/github/build/npm-check-bundles.sh
|
||||
ADF_VERSION=$(npm view @alfresco/adf-core@${TAG_NPM} version)
|
||||
echo "check bundle on TAG_NPM='${TAG_NPM}' and ADF_VERSION='${ADF_VERSION}'"
|
||||
./scripts/github/build/npm-check-bundles.sh -v ${ADF_VERSION}
|
||||
|
||||
@@ -20,13 +20,13 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: install NPM
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache-dependency-path: package-lock.json
|
||||
- name: get latest tag sha
|
||||
id: tag-sha
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/git-latest-tag@a32503e10c10dc999ffcdfa8bc8c5e705cf36928 # v8.31.0
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/git-latest-tag@62d25370db3ca229de518fab5b32ec014a061101 # v8.33.1
|
||||
# CACHE
|
||||
- name: Node Modules cache
|
||||
id: node-modules-cache
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.5
|
||||
uses: github/codeql-action/init@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3.29.5
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
with:
|
||||
languages: javascript
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.5
|
||||
uses: github/codeql-action/autobuild@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3.29.5
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -53,4 +53,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.5
|
||||
uses: github/codeql-action/analyze@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3.29.5
|
||||
|
||||
@@ -29,8 +29,8 @@ env:
|
||||
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
|
||||
NODE_OPTIONS: "--max-old-space-size=5120"
|
||||
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
|
||||
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
|
||||
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
|
||||
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
|
||||
NPM_REGISTRY_ADDRESS: ${{ secrets.NPM_REGISTRY_ADDRESS }}
|
||||
NPM_REGISTRY_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }}
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
with:
|
||||
dry-run-flag: ${{ inputs.dry-run-flag }}
|
||||
- name: install NPM
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
- name: "Release tag"
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
if: github.event.registry_package.package_type == 'npm' && github.event.registry_package.name == 'adf-core'
|
||||
steps:
|
||||
- name: Dispatch event to monorepo
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.PAT_WRITE_PKG }}
|
||||
retries: 3
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Pull Translations from Crowdin
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
- cron: "0 7-17 * * 1-5"
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
pull-from-crowdin:
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Pull translations from Crowdin
|
||||
uses: crowdin/github-action@590c05e09a29f392b203faf4d6aa8e0cd32c7835 # v2.9.1
|
||||
uses: crowdin/github-action@0749939f635900a2521aa6aac7a3766642b2dc71 # v2.11.0
|
||||
with:
|
||||
upload_sources: false
|
||||
download_translations: true
|
||||
|
||||
@@ -46,8 +46,8 @@ env:
|
||||
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
|
||||
NODE_OPTIONS: "--max-old-space-size=5120"
|
||||
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
|
||||
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
|
||||
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
|
||||
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
|
||||
REPO_OWNER: "Alfresco"
|
||||
REPO_NAME: "alfresco-ng2-components"
|
||||
@@ -84,10 +84,10 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get branch name
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/get-branch-name@a32503e10c10dc999ffcdfa8bc8c5e705cf36928 # v8.31.0
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/get-branch-name@62d25370db3ca229de518fab5b32ec014a061101 # v8.33.1
|
||||
|
||||
- name: Save commit message
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/get-commit-message@a32503e10c10dc999ffcdfa8bc8c5e705cf36928 # v8.31.0
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/get-commit-message@62d25370db3ca229de518fab5b32ec014a061101 # v8.33.1
|
||||
|
||||
- name: ci:force flag parser
|
||||
shell: bash
|
||||
@@ -248,7 +248,7 @@ jobs:
|
||||
fetch-depth: 1
|
||||
- name: PR contains forbidden labels
|
||||
id: pr-forbidden
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
const issueHasLabels = require('./scripts/github/update/check-issue-has-label.js');
|
||||
|
||||
@@ -26,8 +26,8 @@ env:
|
||||
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
|
||||
NODE_OPTIONS: "--max-old-space-size=5120"
|
||||
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
|
||||
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
|
||||
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
|
||||
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
REPO_OWNER: "Alfresco"
|
||||
@@ -66,6 +66,8 @@ jobs:
|
||||
|
||||
release-npm:
|
||||
needs: [setup]
|
||||
outputs:
|
||||
release_version: ${{ steps.set-version.outputs.release_version }}
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
@@ -86,11 +88,12 @@ jobs:
|
||||
dry-run-flag: ${{ inputs.dry-run-flag }}
|
||||
- uses: ./.github/actions/download-node-modules-and-artifacts
|
||||
- name: Set libraries versions
|
||||
id: set-version
|
||||
run: |
|
||||
set -u;
|
||||
./scripts/update-version.sh -gnu || exit 1;
|
||||
./scripts/github/build/bumpversion.sh
|
||||
- name: Set migrations
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
const setMigrations = require('./scripts/github/release/set-migrations.js');
|
||||
@@ -99,25 +102,40 @@ jobs:
|
||||
run: |
|
||||
npx nx run-many --target=build --prod --skip-nx-cache
|
||||
npx nx run-many --target=build-schematics
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
name: release libraries GH registry
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
registry-url: 'https://npm.pkg.github.com'
|
||||
scope: '@alfresco'
|
||||
- run: npx nx run-many --target=npm-publish --tag=branch|| exit 1
|
||||
- run: npx nx run-many -t npm-publish --tag=$TAG_NPM || exit 1
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.PAT_WRITE_PKG }}
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
name: release libraries Npm registry
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
registry-url: 'https://${{ vars.NPM_REGISTRY_ADDRESS }}'
|
||||
scope: '@alfresco'
|
||||
- run: npx nx run-many --target=npm-publish --tag=branch || exit 1
|
||||
- run: npx nx run-many -t npm-publish --tag=$TAG_NPM || exit 1
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }}
|
||||
|
||||
create-git-tag:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [setup, release-npm]
|
||||
name: Create github tag
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: './.github/actions/create-git-tag'
|
||||
with:
|
||||
tagName: ${{ needs.release-npm.outputs.release_version }}
|
||||
|
||||
npm-check-bundle:
|
||||
needs: [release-npm]
|
||||
timeout-minutes: 15
|
||||
|
||||
@@ -38,8 +38,8 @@ env:
|
||||
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
|
||||
NODE_OPTIONS: "--max-old-space-size=5120"
|
||||
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
|
||||
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
|
||||
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
|
||||
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
REPO_OWNER: "Alfresco"
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
set -u;
|
||||
./scripts/github/build/bumpversion.sh
|
||||
- name: Set migrations
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
const setMigrations = require('./scripts/github/release/set-migrations.js');
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
run: |
|
||||
npx nx run-many -t build --prod --skip-nx-cache
|
||||
npx nx run-many -t build-schematics
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
name: release libraries GH registry
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
- run: npx nx run-many -t npm-publish --tag=$TAG_NPM || exit 1
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.PAT_WRITE_PKG }}
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
name: release libraries Npm registry
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -215,7 +215,7 @@ jobs:
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Push Source Files to Crowdin
|
||||
uses: crowdin/github-action@590c05e09a29f392b203faf4d6aa8e0cd32c7835 # v2.9.1
|
||||
uses: crowdin/github-action@0749939f635900a2521aa6aac7a3766642b2dc71 # v2.11.0
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_sources_args: --delete-obsolete
|
||||
|
||||
@@ -65,7 +65,7 @@ class AppComponent {
|
||||
|
||||
onLoggedIn() {
|
||||
this.userPreferences.setStoragePrefix(
|
||||
this.authService.getEcmUsername()
|
||||
this.authService.getUsername()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,3 +57,4 @@ The pages linked below contain the licenses for all third party dependencies of
|
||||
- [ADF 7.0.0](license-info-7.0.0.md)
|
||||
- [ADF 8.0.0](license-info-8.0.0.md)
|
||||
- [ADF 8.1.0](license-info-8.1.0.md)
|
||||
- [ADF 8.1.1](license-info-8.1.1.md)
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
Title: License info, alfresco-ng2-components 8.1.1
|
||||
---
|
||||
|
||||
# License information for alfresco-ng2-components 8.1.1
|
||||
|
||||
This page lists all third party libraries the project depends on.
|
||||
|
||||
## Libraries
|
||||
|
||||
| Name | Version | License |
|
||||
| --- | --- | --- |
|
||||
| [@angular/animations](https://github.com/angular/angular) | 19.2.6 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@angular/cdk](https://github.com/angular/components) | 19.2.9 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@angular/common](https://github.com/angular/angular) | 19.2.6 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@angular/compiler](https://github.com/angular/angular) | 19.2.6 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@angular/core](https://github.com/angular/angular) | 19.2.6 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@angular/forms](https://github.com/angular/angular) | 19.2.6 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@angular/material-date-fns-adapter](https://github.com/angular/components) | 19.2.9 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@angular/material](https://github.com/angular/components) | 19.2.9 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@angular/platform-browser-dynamic](https://github.com/angular/angular) | 19.2.6 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@angular/platform-browser](https://github.com/angular/angular) | 19.2.6 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@angular/router](https://github.com/angular/angular) | 19.2.6 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@apollo/client](https://github.com/apollographql/apollo-client) | 3.13.1 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@cspell/eslint-plugin](https://github.com/streetsidesoftware/cspell) | 8.16.1 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@mat-datetimepicker/core](https://github.com/kuhnroyal/mat-datetimepicker) | 15.0.2 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| @ngx-translate/core | 16.0.4 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [@nx/webpack](https://github.com/nrwl/nx) | 20.8.0 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [angular-oauth2-oidc-jwks](https://github.com/manfredsteyer/angular-oauth2-oidc) | 17.0.2 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [angular-oauth2-oidc](https://github.com/manfredsteyer/angular-oauth2-oidc) | 17.0.2 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [apollo-angular](https://github.com/kamilkisiela/apollo-angular) | 10.0.3 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [chart.js](https://github.com/chartjs/Chart.js) | 4.4.4 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [cropperjs](https://github.com/fengyuanchen/cropperjs) | 1.6.2 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [date-fns](https://github.com/date-fns/date-fns) | 2.30.0 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [dotenv-expand](https://github.com/motdotla/dotenv-expand) | 11.0.7 | [BSD-2-Clause](http://www.opensource.org/licenses/BSD-2-Clause) |
|
||||
| dotenv-expand | 5.1.0 | [BSD-2-Clause](http://www.opensource.org/licenses/BSD-2-Clause) |
|
||||
| [event-emitter](https://github.com/medikoo/event-emitter) | 0.3.5 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [graphql-ws](https://github.com/enisdenjo/graphql-ws) | 6.0.6 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [material-icons](https://github.com/marella/material-icons) | 1.13.14 | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) |
|
||||
| [minimatch-browser](https://github.com/isaacs/minimatch) | 1.0.0 | [ISC](https://www.isc.org/downloads/software-support-policy/isc-license/) |
|
||||
| [ng2-charts](https://github.com/valor-software/ng2-charts) | 4.1.1 | [ISC](https://www.isc.org/downloads/software-support-policy/isc-license/) |
|
||||
| [node-fetch](https://github.com/node-fetch/node-fetch) | 3.3.2 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [pdfjs-dist](https://github.com/mozilla/pdf.js) | 5.1.91 | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) |
|
||||
| [raphael](https://github.com/DmitryBaranovskiy/raphael) | 2.3.0 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [rxjs](https://github.com/reactivex/rxjs) | 7.8.2 | [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0) |
|
||||
| [superagent](https://github.com/ladjs/superagent) | 9.0.2 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [ts-morph](https://github.com/dsherret/ts-morph) | 26.0.0 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
| [tslib](https://github.com/Microsoft/tslib) | 2.8.1 | [0BSD](http://landley.net/toybox/license.html) |
|
||||
| [zone.js](https://github.com/angular/angular) | 0.15.0 | [MIT](http://www.opensource.org/licenses/MIT) |
|
||||
|
||||
@@ -9,6 +9,7 @@ The first **General Availability** release was v2.0.0.
|
||||
|
||||
## General Availability
|
||||
|
||||
- [8.1.1](RelNote-8.1.1.md)
|
||||
- [8.1.0](RelNote-8.1.0.md)
|
||||
- [8.0.0](RelNote-8.0.0.md)
|
||||
- [7.0.0](RelNote-7.0.0.md)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
Title: Changelog for alfresco-ng2-components v8.1.1
|
||||
---
|
||||
|
||||
# Changelog
|
||||
|
||||
- [0576aac985](https://github.com/Alfresco/alfresco-ng2-components/commit/0576aac985) Revert "CSX-73 Date and Select Satori (#11056)" (#11144)
|
||||
@@ -19,14 +19,24 @@
|
||||
|
||||
4. Apply and commit your fix to **develop-patch-VERSION** branch.
|
||||
|
||||
5. Run [release.sh](../../scripts/release.sh) script with proper patch version:
|
||||
5. Increment the version in the package.json with the following command where `-v` refers to the ADF version and `-vj` to the new js-api version.
|
||||
The js-api must have a major version that is one higher than the other packages. For example, if adf-core is at version `4.11.1`, then js-api should be at version `5.11.1`
|
||||
|
||||
```bash
|
||||
./scripts/update-version.sh -v X.X.X -vj X.X.X
|
||||
|
||||
# e.g. ./scripts/update-version.sh -v 4.1.11 -vj 5.1.11
|
||||
```
|
||||
6. The `"@alfresco/js-api"` dependency in the `lib/cli/package.json` has to remain unchanged
|
||||
|
||||
7. Run [release.sh](../../scripts/release.sh) script with proper patch version:
|
||||
|
||||
```bash
|
||||
scripts/release.sh -v VERSION
|
||||
|
||||
# e.g. scripts/release.sh -v 4.11.1
|
||||
```
|
||||
6. Push your changes and run all tests:
|
||||
8. Push your changes and run all tests:
|
||||
|
||||
```bash
|
||||
git push -u origin develop-patch-VERSION
|
||||
@@ -39,28 +49,28 @@
|
||||
git push
|
||||
```
|
||||
|
||||
7. Verify if tests are green and if everything looks fine, you can proceed further.
|
||||
9. Verify if tests are green and if everything looks fine, you can proceed further.
|
||||
|
||||
8. Create new branch from **master** and call it **master-patch-VERSION** (e.g. master-patch-4.11.1):
|
||||
10. Create new branch from **master** and call it **master-patch-VERSION** (e.g. master-patch-4.11.1):
|
||||
|
||||
```bash
|
||||
git checkout master
|
||||
git checkout -b master-patch-VERSION
|
||||
```
|
||||
|
||||
9. Merge **develop-patch-VERSION** into **master-patch-VERSION**:
|
||||
11. Merge **develop-patch-VERSION** into **master-patch-VERSION**:
|
||||
|
||||
```bash
|
||||
git checkout master-patch-VERSION
|
||||
git merge develop-patch-VERSION
|
||||
```
|
||||
|
||||
10. Push **master-patch-VERSION** branch:
|
||||
12. Push **master-patch-VERSION** branch:
|
||||
|
||||
```bash
|
||||
git push -u origin master-patch-VERSION
|
||||
```
|
||||
|
||||
11. Verify if build is green and check if proper [tag](https://github.com/Alfresco/alfresco-ng2-components/tags) was created.
|
||||
13. Verify if build is green and check if proper [tag](https://github.com/Alfresco/alfresco-ng2-components/tags) was created.
|
||||
|
||||
12. After all is done, you can **cherry-pick** your fix to develop.
|
||||
14. After all is done, you can **cherry-pick** your fix to develop.
|
||||
|
||||
@@ -54,3 +54,4 @@ The pages linked below contain the audit for all third party dependencies of ADF
|
||||
- [ADF 7.0.0](audit-info-7.0.0.md)
|
||||
- [ADF 8.0.0](audit-info-8.0.0.md)
|
||||
- [ADF 8.1.0](audit-info-8.1.0.md)
|
||||
- [ADF 8.1.1](audit-info-8.1.1.md)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
Title: Audit info, alfresco-ng2-components 8.1.1
|
||||
---
|
||||
|
||||
# Audit information for alfresco-ng2-components 8.1.1
|
||||
|
||||
This page lists the security audit of the dependencies this project depends on.
|
||||
|
||||
## Risks
|
||||
|
||||
- Critical risk: 0
|
||||
- High risk: 0
|
||||
- Moderate risk: 0
|
||||
- Low risk: 0
|
||||
|
||||
Dependencies analyzed:
|
||||
|
||||
## Libraries
|
||||
|
||||
| Severity | Module | Vulnerable versions |
|
||||
| --- | --- | --- |
|
||||
|
||||
|
||||
Generated
+6
-6
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "@alfresco/adf-cli",
|
||||
"version": "8.1.0",
|
||||
"version": "8.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@alfresco/adf-cli",
|
||||
"version": "8.1.0",
|
||||
"version": "8.3.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@alfresco/js-api": ">=9.0.0",
|
||||
"@alfresco/js-api": ">=9.1.1",
|
||||
"commander": "^6.2.1",
|
||||
"ejs": "^3.1.9",
|
||||
"license-checker": "^25.0.1",
|
||||
@@ -30,9 +30,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@alfresco/js-api": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@alfresco/js-api/-/js-api-9.0.0.tgz",
|
||||
"integrity": "sha512-tkVc3vCmbyp1cAJOtga/c5kApWmkj7lBmnJk0M+Jx/aek5mUQkXvDKlpPBoE0QR8UHKEKgB6HgwmaOMumYQVAw==",
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@alfresco/js-api/-/js-api-9.1.1.tgz",
|
||||
"integrity": "sha512-G45OueaXjy0LxUv0RuQb6xfiEf/ecVX2K5qgfMdKlDcmXSv2k8lL/m+wUqeVw1NC+EeoSg6hMyOH60Y3qLvTuQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"event-emitter": "^0.3.5",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@alfresco/adf-cli",
|
||||
"description": "Alfresco ADF cli and utils",
|
||||
"version": "8.1.0",
|
||||
"version": "8.3.0",
|
||||
"author": "Hyland Software, Inc. and its affiliates",
|
||||
"bin": {
|
||||
"adf-cli": "bin/adf-cli",
|
||||
@@ -20,7 +20,7 @@
|
||||
"dist": "rm -rf ../../dist/libs/cli && npm run build && cp -R ./bin ../../dist/libs/cli && cp -R ./resources ../../dist/libs/cli && cp -R ./templates ../../dist/libs/cli && cp ./package.json ../../dist/libs/cli"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alfresco/js-api": ">=9.0.0",
|
||||
"@alfresco/js-api": ">=9.1.1",
|
||||
"commander": "^6.2.1",
|
||||
"ejs": "^3.1.9",
|
||||
"license-checker": "^25.0.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@alfresco/adf-content-services",
|
||||
"description": "Alfresco ADF content services",
|
||||
"version": "8.1.0",
|
||||
"version": "8.3.0",
|
||||
"author": "Hyland Software, Inc. and its affiliates",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,9 +21,9 @@
|
||||
"@angular/platform-browser": ">=14.1.3",
|
||||
"@angular/platform-browser-dynamic": ">=14.1.3",
|
||||
"@angular/router": ">=14.1.3",
|
||||
"@alfresco/js-api": ">=9.1.0",
|
||||
"@alfresco/js-api": ">=9.1.1",
|
||||
"@ngx-translate/core": ">=16.0.0",
|
||||
"@alfresco/adf-core": ">=8.1.0"
|
||||
"@alfresco/adf-core": ">=8.2.0"
|
||||
},
|
||||
"keywords": [
|
||||
"content-services",
|
||||
|
||||
@@ -38,11 +38,14 @@ export class DiscoveryApiService {
|
||||
*/
|
||||
ecmProductInfo$ = new Subject<RepositoryInfo>();
|
||||
|
||||
constructor(private authenticationService: AuthenticationService, private alfrescoApiService: AlfrescoApiService) {
|
||||
constructor(
|
||||
private readonly authenticationService: AuthenticationService,
|
||||
private readonly alfrescoApiService: AlfrescoApiService
|
||||
) {
|
||||
this.authenticationService.onLogin.subscribe(() => {
|
||||
this.alfrescoApiService.alfrescoApiInitialized
|
||||
.pipe(
|
||||
filter(() => this.authenticationService.isEcmLoggedIn()),
|
||||
filter(() => this.authenticationService.isLoggedIn()),
|
||||
take(1),
|
||||
switchMap(() => this.getEcmProductInfo())
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ import { PeopleContentQueryRequestModel, PeopleContentService } from './people-c
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { PersonPaging } from '@alfresco/js-api';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { EMPTY, of } from 'rxjs';
|
||||
import { EMPTY, firstValueFrom, of } from 'rxjs';
|
||||
import { AlfrescoApiService } from '../../services';
|
||||
import { AlfrescoApiServiceMock } from '../../mock';
|
||||
|
||||
@@ -83,14 +83,14 @@ describe('PeopleContentService', () => {
|
||||
|
||||
it('should be able to fetch person details based on id', async () => {
|
||||
spyOn(peopleContentService.peopleApi, 'getPerson').and.returnValue(Promise.resolve({ entry: fakeEcmUser } as any));
|
||||
const person = await peopleContentService.getPerson('fake-id').toPromise();
|
||||
const person = await firstValueFrom(peopleContentService.getPerson('fake-id'));
|
||||
expect(person.id).toEqual('fake-id');
|
||||
expect(person.email).toEqual('fakeEcm@ecmUser.com');
|
||||
});
|
||||
|
||||
it('should be able to list people', async () => {
|
||||
spyOn(peopleContentService.peopleApi, 'listPeople').and.returnValue(Promise.resolve(fakeEcmUserList));
|
||||
const response = await peopleContentService.listPeople().toPromise();
|
||||
const response = await firstValueFrom(peopleContentService.listPeople());
|
||||
const people = response.entries;
|
||||
const pagination = response.pagination;
|
||||
|
||||
@@ -112,7 +112,7 @@ describe('PeopleContentService', () => {
|
||||
};
|
||||
const expectedValue = { skipCount: 10, maxItems: 20, orderBy: ['firstName ASC'] } as any;
|
||||
|
||||
await peopleContentService.listPeople(requestQueryParams).toPromise();
|
||||
await firstValueFrom(peopleContentService.listPeople(requestQueryParams));
|
||||
|
||||
expect(listPeopleSpy).toHaveBeenCalledWith(expectedValue);
|
||||
});
|
||||
@@ -122,14 +122,14 @@ describe('PeopleContentService', () => {
|
||||
const requestQueryParams: PeopleContentQueryRequestModel = { skipCount: 10, maxItems: 20, sorting: undefined };
|
||||
const expectedValue = { skipCount: 10, maxItems: 20 };
|
||||
|
||||
await peopleContentService.listPeople(requestQueryParams).toPromise();
|
||||
await firstValueFrom(peopleContentService.listPeople(requestQueryParams));
|
||||
|
||||
expect(listPeopleSpy).toHaveBeenCalledWith(expectedValue);
|
||||
});
|
||||
|
||||
it('should be able to create new person', async () => {
|
||||
spyOn(peopleContentService.peopleApi, 'createPerson').and.returnValue(Promise.resolve({ entry: fakeEcmUser } as any));
|
||||
const newUser = await peopleContentService.createPerson(createNewPersonMock).toPromise();
|
||||
const newUser = await firstValueFrom(peopleContentService.createPerson(createNewPersonMock));
|
||||
expect(newUser.id).toEqual('fake-id');
|
||||
expect(newUser.email).toEqual('fakeEcm@ecmUser.com');
|
||||
});
|
||||
@@ -150,12 +150,12 @@ describe('PeopleContentService', () => {
|
||||
Promise.resolve({ entry: fakeEcmAdminUser } as any)
|
||||
);
|
||||
|
||||
const user = await peopleContentService.getCurrentUserInfo().toPromise();
|
||||
const user = await firstValueFrom(peopleContentService.getCurrentUserInfo());
|
||||
expect(user.id).toEqual('fake-id');
|
||||
expect(peopleContentService.isCurrentUserAdmin()).toBe(true);
|
||||
expect(getCurrentPersonSpy.calls.count()).toEqual(1);
|
||||
|
||||
await peopleContentService.getCurrentUserInfo().toPromise();
|
||||
await firstValueFrom(peopleContentService.getCurrentUserInfo());
|
||||
|
||||
expect(peopleContentService.isCurrentUserAdmin()).toBe(true);
|
||||
expect(getCurrentPersonSpy.calls.count()).toEqual(1);
|
||||
@@ -165,13 +165,13 @@ describe('PeopleContentService', () => {
|
||||
const getCurrentPersonSpy = spyOn(peopleContentService.peopleApi, 'getPerson').and.returnValue(
|
||||
Promise.resolve({ entry: fakeEcmAdminUser } as any)
|
||||
);
|
||||
await peopleContentService.getCurrentUserInfo().toPromise();
|
||||
await firstValueFrom(peopleContentService.getCurrentUserInfo());
|
||||
|
||||
getCurrentPersonSpy.and.returnValue(Promise.resolve({ entry: fakeEcmUser2 } as any));
|
||||
await peopleContentService.getPerson('fake-id').toPromise();
|
||||
await firstValueFrom(peopleContentService.getPerson('fake-id'));
|
||||
|
||||
expect(getCurrentPersonSpy.calls.count()).toEqual(2);
|
||||
const currentUser = await peopleContentService.getCurrentUserInfo().toPromise();
|
||||
const currentUser = await firstValueFrom(peopleContentService.getCurrentUserInfo());
|
||||
expect(peopleContentService.isCurrentUserAdmin()).toBe(true);
|
||||
expect(currentUser.id).toEqual(fakeEcmAdminUser.id);
|
||||
expect(currentUser.id).not.toEqual(fakeEcmUser2.id);
|
||||
|
||||
+4
-33
@@ -198,7 +198,7 @@
|
||||
</mat-expansion-panel>
|
||||
|
||||
<ng-container *ngIf="groupedProperties$ | async; else loading; let groupedProperties">
|
||||
<div *ngFor="let group of groupedProperties; let first = first" class="adf-metadata-grouped-properties-container">
|
||||
<div *ngFor="let group of groupedProperties; let first = first" class="adf-content-metadata-ai">
|
||||
<mat-expansion-panel
|
||||
[attr.data-automation-id]="'adf-metadata-group-' + group.title"
|
||||
[expanded]="multi ? !displayDefaultProperties && first || group.expanded : !displayDefaultProperties && first || currentPanel.panelTitle === group.title && currentPanel.expanded"
|
||||
@@ -209,37 +209,7 @@
|
||||
<mat-expansion-panel-header
|
||||
class="adf-metadata-properties-header"
|
||||
[class.adf-metadata-properties-header-expanded]="currentPanel.panelTitle === group.title && currentPanel.expanded">
|
||||
<adf-content-metadata-header [title]="group.title" [expanded]="currentPanel.panelTitle === group.title && currentPanel.expanded">
|
||||
<button
|
||||
*ngIf="group.editable && !this.readOnly && !isPanelEditing(group.title)"
|
||||
mat-icon-button
|
||||
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
|
||||
[attr.aria-label]="'CORE.METADATA.ACCESSIBILITY.EDIT' | translate"
|
||||
data-automation-id="meta-data-card-toggle-edit"
|
||||
class="adf-edit-icon-buttons"
|
||||
(click)="toggleGroupEditing(group.title, $event)">
|
||||
<mat-icon>mode_edit</mat-icon>
|
||||
</button>
|
||||
<div class="adf-metadata-action-buttons" *ngIf="group.editable && isPanelEditing(group.title)">
|
||||
<button
|
||||
mat-icon-button
|
||||
[attr.title]="'CORE.METADATA.ACTIONS.CANCEL' | translate"
|
||||
(click)="cancelGroupEditing(group.title, $event)"
|
||||
data-automation-id="reset-metadata"
|
||||
class="adf-metadata-action-buttons-clear">
|
||||
<mat-icon>clear</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
mat-icon-button
|
||||
[attr.title]="'CORE.METADATA.ACTIONS.SAVE' | translate"
|
||||
(click)="saveChanges($event)"
|
||||
color="primary"
|
||||
data-automation-id="save-metadata"
|
||||
[disabled]="!hasMetadataChanged || invalidProperties.size > 0">
|
||||
<mat-icon>check</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</adf-content-metadata-header>
|
||||
<adf-content-metadata-header [title]="group.title" [expanded]="currentPanel.panelTitle === group.title && currentPanel.expanded" />
|
||||
</mat-expansion-panel-header>
|
||||
<div *ngIf="!showGroup(group) && group.editable && editedPanelTitle !== group.title" class="adf-metadata-no-item-added">
|
||||
{{ 'METADATA.BASIC.NO_ITEMS_MESSAGE' | translate : { groupTitle: group.title | translate } }}
|
||||
@@ -247,7 +217,7 @@
|
||||
<adf-card-view
|
||||
(keydown)="keyDown($event)"
|
||||
[properties]="group.properties"
|
||||
[editable]="!readOnly && group.editable && isPanelEditing(group.title)"
|
||||
[editable]="false"
|
||||
[displayEmpty]="displayEmpty"
|
||||
[copyToClipboardAction]="copyToClipboardAction"
|
||||
[useChipsForMultiValueProperty]="useChipsForMultiValueProperty"
|
||||
@@ -257,6 +227,7 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
<ng-template #loading>
|
||||
<mat-progress-bar mode="indeterminate" [attr.aria-label]="'DATA_LOADING' | translate" />
|
||||
</ng-template>
|
||||
|
||||
+8
-8
@@ -32,7 +32,7 @@ import { MatChipHarness } from '@angular/material/chips/testing';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatExpansionPanel } from '@angular/material/expansion';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { EMPTY, of, throwError } from 'rxjs';
|
||||
import { EMPTY, firstValueFrom, of, throwError } from 'rxjs';
|
||||
import { CategoriesManagementComponent, CategoriesManagementMode } from '../../../category';
|
||||
import { TagsCreatorComponent, TagsCreatorMode } from '../../../tag';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
@@ -1144,7 +1144,7 @@ describe('ContentMetadataComponent', () => {
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
fixture.detectChanges();
|
||||
|
||||
await component.groupedProperties$.toPromise();
|
||||
await firstValueFrom(component.groupedProperties$);
|
||||
fixture.detectChanges();
|
||||
|
||||
const verProp = queryDom('Versionable');
|
||||
@@ -1164,7 +1164,7 @@ describe('ContentMetadataComponent', () => {
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
fixture.detectChanges();
|
||||
|
||||
await component.groupedProperties$.toPromise();
|
||||
await firstValueFrom(component.groupedProperties$);
|
||||
fixture.detectChanges();
|
||||
|
||||
const verProps = fixture.debugElement.queryAll(By.css('[data-automation-id="adf-metadata-group-Versionable"]'));
|
||||
@@ -1184,7 +1184,7 @@ describe('ContentMetadataComponent', () => {
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
fixture.detectChanges();
|
||||
|
||||
await component.groupedProperties$.toPromise();
|
||||
await firstValueFrom(component.groupedProperties$);
|
||||
fixture.detectChanges();
|
||||
|
||||
const verProp = queryDom('Versionable');
|
||||
@@ -1205,7 +1205,7 @@ describe('ContentMetadataComponent', () => {
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
fixture.detectChanges();
|
||||
|
||||
await component.groupedProperties$.toPromise();
|
||||
await firstValueFrom(component.groupedProperties$);
|
||||
fixture.detectChanges();
|
||||
|
||||
const verProp = queryDom('Versionable');
|
||||
@@ -1225,7 +1225,7 @@ describe('ContentMetadataComponent', () => {
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
fixture.detectChanges();
|
||||
|
||||
await component.groupedProperties$.toPromise();
|
||||
await firstValueFrom(component.groupedProperties$);
|
||||
fixture.detectChanges();
|
||||
|
||||
const verProp = queryDom('Versionable');
|
||||
@@ -1249,7 +1249,7 @@ describe('ContentMetadataComponent', () => {
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
fixture.detectChanges();
|
||||
|
||||
await component.groupedProperties$.toPromise();
|
||||
await firstValueFrom(component.groupedProperties$);
|
||||
fixture.detectChanges();
|
||||
|
||||
const exifProp = queryDom('Exif');
|
||||
@@ -1283,7 +1283,7 @@ describe('ContentMetadataComponent', () => {
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
fixture.detectChanges();
|
||||
|
||||
await component.groupedProperties$.toPromise();
|
||||
await firstValueFrom(component.groupedProperties$);
|
||||
fixture.detectChanges();
|
||||
|
||||
const exifProps = fixture.debugElement.queryAll(By.css('[data-automation-id="adf-metadata-group-Exif"]'));
|
||||
|
||||
+10
-10
@@ -18,7 +18,7 @@
|
||||
import { AppConfigService } from '@alfresco/adf-core';
|
||||
import { ClassesApi, Node } from '@alfresco/js-api';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { of } from 'rxjs';
|
||||
import { firstValueFrom, of } from 'rxjs';
|
||||
import { ContentTestingModule } from '../../testing/content.testing.module';
|
||||
import { OrganisedPropertyGroup } from '../interfaces/content-metadata.interfaces';
|
||||
import { PropertyGroup } from '../interfaces/property-group.interface';
|
||||
@@ -253,7 +253,7 @@ describe('ContentMetaDataService', () => {
|
||||
|
||||
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(exifResponse));
|
||||
|
||||
const groupedProperties = await service.getGroupedProperties(fakeNode).toPromise();
|
||||
const groupedProperties = await firstValueFrom(service.getGroupedProperties(fakeNode));
|
||||
|
||||
expect(groupedProperties.length).toEqual(1);
|
||||
expect(groupedProperties[0].title).toEqual('Exif');
|
||||
@@ -267,7 +267,7 @@ describe('ContentMetaDataService', () => {
|
||||
|
||||
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(exifResponse));
|
||||
|
||||
const groupedProperties = await service.getGroupedProperties(fakeNode).toPromise();
|
||||
const groupedProperties = await firstValueFrom(service.getGroupedProperties(fakeNode));
|
||||
|
||||
expect(groupedProperties.length).toEqual(1);
|
||||
expect(groupedProperties[0].title).toEqual('Exif');
|
||||
@@ -284,7 +284,7 @@ describe('ContentMetaDataService', () => {
|
||||
|
||||
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(verResponse));
|
||||
|
||||
const groupedProperties = await service.getGroupedProperties(fakeContentNode).toPromise();
|
||||
const groupedProperties = await firstValueFrom(service.getGroupedProperties(fakeContentNode));
|
||||
|
||||
expect(groupedProperties.length).toEqual(1);
|
||||
expect(groupedProperties[0].title).toEqual('Versionable');
|
||||
@@ -301,7 +301,7 @@ describe('ContentMetaDataService', () => {
|
||||
|
||||
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(verResponse));
|
||||
|
||||
const groupedProperties = await service.getGroupedProperties(fakeContentNode).toPromise();
|
||||
const groupedProperties = await firstValueFrom(service.getGroupedProperties(fakeContentNode));
|
||||
|
||||
expect(groupedProperties.length).toEqual(2);
|
||||
expect(groupedProperties[0].title).toEqual('Versionable');
|
||||
@@ -319,7 +319,7 @@ describe('ContentMetaDataService', () => {
|
||||
|
||||
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(verResponse));
|
||||
|
||||
const groupedProperties = await service.getGroupedProperties(fakeContentNode).toPromise();
|
||||
const groupedProperties = await firstValueFrom(service.getGroupedProperties(fakeContentNode));
|
||||
expect(groupedProperties.length).toEqual(0);
|
||||
|
||||
expect(classesApi.getClass).toHaveBeenCalledTimes(1 + fakeContentNode.aspectNames.length);
|
||||
@@ -335,7 +335,7 @@ describe('ContentMetaDataService', () => {
|
||||
|
||||
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(verResponse));
|
||||
|
||||
const groupedProperties = await service.getGroupedProperties(fakeContentNode).toPromise();
|
||||
const groupedProperties = await firstValueFrom(service.getGroupedProperties(fakeContentNode));
|
||||
expect(groupedProperties.length).toEqual(1);
|
||||
expect(groupedProperties[0].title).toEqual('Versionable');
|
||||
|
||||
@@ -351,7 +351,7 @@ describe('ContentMetaDataService', () => {
|
||||
|
||||
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(verResponse));
|
||||
|
||||
const groupedProperties = await service.getGroupedProperties(fakeContentNode).toPromise();
|
||||
const groupedProperties = await firstValueFrom(service.getGroupedProperties(fakeContentNode));
|
||||
expect(groupedProperties.length).toEqual(0);
|
||||
|
||||
expect(classesApi.getClass).toHaveBeenCalledTimes(1 + fakeContentNode.aspectNames.length);
|
||||
@@ -367,7 +367,7 @@ describe('ContentMetaDataService', () => {
|
||||
|
||||
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(exifResponse));
|
||||
|
||||
const groupedProperties = await service.getGroupedProperties(fakeNode).toPromise();
|
||||
const groupedProperties = await firstValueFrom(service.getGroupedProperties(fakeNode));
|
||||
|
||||
expect(groupedProperties.length).toEqual(1);
|
||||
expect(groupedProperties[0].title).toEqual('Exif');
|
||||
@@ -387,7 +387,7 @@ describe('ContentMetaDataService', () => {
|
||||
|
||||
spyOn(classesApi, 'getClass').and.returnValue(Promise.resolve(exifResponse));
|
||||
|
||||
const groupedProperties = await service.getGroupedProperties(fakeNode).toPromise();
|
||||
const groupedProperties = await firstValueFrom(service.getGroupedProperties(fakeNode));
|
||||
|
||||
expect(groupedProperties.length).toEqual(2);
|
||||
expect(groupedProperties[0].title).toEqual('Exif');
|
||||
|
||||
@@ -36,7 +36,6 @@ class AlfrescoApiMock {
|
||||
isOauthConfiguration = () => true;
|
||||
isLoggedIn = () => true;
|
||||
isEcmConfiguration = () => true;
|
||||
isEcmLoggedIn = () => true;
|
||||
}
|
||||
|
||||
export class ContentApiMock {
|
||||
|
||||
@@ -50,7 +50,7 @@ describe('NodeDownloadDirective', () => {
|
||||
oauth2Auth: {
|
||||
callCustomApi: () => Promise.resolve()
|
||||
},
|
||||
isEcmLoggedIn: jasmine.createSpy('isEcmLoggedIn'),
|
||||
isLoggedIn: jasmine.createSpy('isLoggedIn'),
|
||||
reply: jasmine.createSpy('reply')
|
||||
};
|
||||
|
||||
|
||||
+6
-6
@@ -18,7 +18,7 @@
|
||||
import { Component, EventEmitter, Output } from '@angular/core';
|
||||
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
|
||||
import { BehaviorSubject, of, Subject } from 'rxjs';
|
||||
import { BehaviorSubject, firstValueFrom, of, Subject } from 'rxjs';
|
||||
import { mockFile, mockNewVersionUploaderData, mockNode } from '../mock';
|
||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||
import {
|
||||
@@ -114,7 +114,7 @@ describe('NewVersionUploaderService', () => {
|
||||
});
|
||||
|
||||
it('should open dialog with default configuration', fakeAsync(() => {
|
||||
service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData).toPromise();
|
||||
firstValueFrom(service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData));
|
||||
tick();
|
||||
expect(spyOnDialogOpen).toHaveBeenCalledWith(NewVersionUploaderDialogComponent, expectedConfig);
|
||||
}));
|
||||
@@ -124,7 +124,7 @@ describe('NewVersionUploaderService', () => {
|
||||
panelClass: 'adf-custom-class',
|
||||
width: '500px'
|
||||
};
|
||||
service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData, mockDialogConfiguration).toPromise();
|
||||
firstValueFrom(service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData, mockDialogConfiguration));
|
||||
tick();
|
||||
expectedConfig.panelClass = 'adf-custom-class';
|
||||
expectedConfig.width = '500px';
|
||||
@@ -135,7 +135,7 @@ describe('NewVersionUploaderService', () => {
|
||||
const mockDialogConfiguration: MatDialogConfig = {
|
||||
height: '600px'
|
||||
};
|
||||
service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData, mockDialogConfiguration).toPromise();
|
||||
firstValueFrom(service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData, mockDialogConfiguration));
|
||||
tick();
|
||||
expectedConfig.height = '600px';
|
||||
expect(spyOnDialogOpen).toHaveBeenCalledWith(NewVersionUploaderDialogComponent, expectedConfig);
|
||||
@@ -143,7 +143,7 @@ describe('NewVersionUploaderService', () => {
|
||||
|
||||
it('should not override dialog configuration, if dialog configuration is empty', fakeAsync(() => {
|
||||
const mockDialogConfiguration: MatDialogConfig = {};
|
||||
service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData, mockDialogConfiguration).toPromise();
|
||||
firstValueFrom(service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData, mockDialogConfiguration));
|
||||
tick();
|
||||
expect(spyOnDialogOpen).toHaveBeenCalledWith(NewVersionUploaderDialogComponent, expectedConfig);
|
||||
}));
|
||||
@@ -156,7 +156,7 @@ describe('NewVersionUploaderService', () => {
|
||||
showComments: true,
|
||||
allowDownload: true
|
||||
};
|
||||
service.openUploadNewVersionDialog(mockNewVersionUploaderDialogDataWithVersionsOnly).toPromise();
|
||||
firstValueFrom(service.openUploadNewVersionDialog(mockNewVersionUploaderDialogDataWithVersionsOnly));
|
||||
tick();
|
||||
expectedConfig.data.showVersionsOnly = true;
|
||||
expectedConfig.panelClass = ['adf-new-version-uploader-dialog', 'adf-new-version-uploader-dialog-list'];
|
||||
|
||||
@@ -69,7 +69,7 @@ describe('SearchControlComponent', () => {
|
||||
searchService = TestBed.inject(SearchService);
|
||||
authService = TestBed.inject(AuthenticationService);
|
||||
userPreferencesService = TestBed.inject(UserPreferencesService);
|
||||
spyOn(authService, 'isEcmLoggedIn').and.returnValue(true);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(true);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
|
||||
|
||||
@@ -109,13 +109,16 @@ export class SearchControlComponent {
|
||||
noSearchResultTemplate: TemplateRef<any> = null;
|
||||
searchTerm: string = '';
|
||||
|
||||
constructor(public authService: AuthenticationService, private thumbnailService: ThumbnailService) {}
|
||||
constructor(
|
||||
public authService: AuthenticationService,
|
||||
private readonly thumbnailService: ThumbnailService
|
||||
) {}
|
||||
|
||||
isNoSearchTemplatePresent(): boolean {
|
||||
return !!this.emptySearchTemplate;
|
||||
}
|
||||
isLoggedIn(): boolean {
|
||||
return this.authService.isEcmLoggedIn();
|
||||
return this.authService.isLoggedIn();
|
||||
}
|
||||
|
||||
inputChange(value: string) {
|
||||
|
||||
+7
-6
@@ -21,6 +21,7 @@ import { fakeAuthorityClearanceApiResponse } from './mock/security-authorities.m
|
||||
import { fakeGroupsApiResponse, createNewSecurityGroupMock } from './mock/security-groups.mock';
|
||||
import { fakeMarksApiResponse, createNewSecurityMarkMock } from './mock/security-marks.mock';
|
||||
import { SecurityGroupBody, SecurityMarkBody, SecurityMarkEntry } from '@alfresco/js-api';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
describe('SecurityControlsService', () => {
|
||||
let service: SecurityControlsService;
|
||||
@@ -78,7 +79,7 @@ describe('SecurityControlsService', () => {
|
||||
}
|
||||
})
|
||||
);
|
||||
const response = await service.createSecurityGroup(createNewSecurityGroupMock).toPromise();
|
||||
const response = await firstValueFrom(service.createSecurityGroup(createNewSecurityGroupMock));
|
||||
|
||||
securityGroupId = response.entry.id;
|
||||
expect(response.entry.groupName).toEqual('TestGroup');
|
||||
@@ -176,7 +177,7 @@ describe('SecurityControlsService', () => {
|
||||
|
||||
it('should delete a security group', async () => {
|
||||
spyOn(service.groupsApi, 'deleteSecurityGroup').and.returnValue(Promise.resolve());
|
||||
await service.deleteSecurityGroup(securityGroupId).toPromise();
|
||||
await firstValueFrom(service.deleteSecurityGroup(securityGroupId));
|
||||
expect(service.groupsApi.deleteSecurityGroup).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -185,7 +186,7 @@ describe('SecurityControlsService', () => {
|
||||
Promise.resolve(fakeAuthorityClearanceApiResponse)
|
||||
);
|
||||
const clearancePromise = service.getClearancesForAuthority('test-id', 0, 10);
|
||||
const clearance = await clearancePromise.toPromise();
|
||||
const clearance = await firstValueFrom(clearancePromise);
|
||||
|
||||
expect(getClearancesForAuthoritySpy).toHaveBeenCalledWith('test-id', {
|
||||
skipCount: 0,
|
||||
@@ -210,15 +211,15 @@ describe('SecurityControlsService', () => {
|
||||
}
|
||||
})
|
||||
);
|
||||
const response = (await service
|
||||
.updateClearancesForAuthority('test-id', [
|
||||
const response = (await firstValueFrom(
|
||||
service.updateClearancesForAuthority('test-id', [
|
||||
{
|
||||
groupId: 'test-group-id',
|
||||
op: 'test-op',
|
||||
id: 'test-id'
|
||||
}
|
||||
])
|
||||
.toPromise()) as SecurityMarkEntry;
|
||||
)) as SecurityMarkEntry;
|
||||
|
||||
expect(response.entry.id).toEqual('test-id');
|
||||
expect(response.entry.groupId).toEqual('test-groupId');
|
||||
|
||||
@@ -19,7 +19,7 @@ import { AppConfigService, UserPreferencesService } from '@alfresco/adf-core';
|
||||
import { TagService } from './tag.service';
|
||||
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { ContentTestingModule } from '../../testing/content.testing.module';
|
||||
import { throwError } from 'rxjs';
|
||||
import { firstValueFrom, throwError } from 'rxjs';
|
||||
import { Pagination, Tag, TagBody, TagEntry, TagPaging, TagPagingList } from '@alfresco/js-api';
|
||||
|
||||
describe('TagService', () => {
|
||||
@@ -75,7 +75,7 @@ describe('TagService', () => {
|
||||
let lastValue: any;
|
||||
service.refresh.subscribe((res) => (lastValue = res));
|
||||
|
||||
await service.addTag('fake-node-id', 'fake-tag').toPromise();
|
||||
await firstValueFrom(service.addTag('fake-node-id', 'fake-tag'));
|
||||
expect(lastValue).toBe(tagEntry);
|
||||
});
|
||||
|
||||
@@ -83,7 +83,7 @@ describe('TagService', () => {
|
||||
let lastValue = false;
|
||||
service.refresh.subscribe(() => (lastValue = true));
|
||||
|
||||
await service.deleteTag('fake-tag-id').toPromise();
|
||||
await firstValueFrom(service.deleteTag('fake-tag-id'));
|
||||
expect(lastValue).toBeTrue();
|
||||
});
|
||||
|
||||
@@ -110,7 +110,7 @@ describe('TagService', () => {
|
||||
spyOn(service.refresh, 'emit');
|
||||
spyOn(service.tagsApi, 'createTags').and.returnValue(Promise.resolve(tag));
|
||||
|
||||
await service.createTags([]).toPromise();
|
||||
await firstValueFrom(service.createTags([]));
|
||||
expect(service.refresh.emit).toHaveBeenCalledWith(tag);
|
||||
});
|
||||
});
|
||||
@@ -248,7 +248,7 @@ describe('TagService', () => {
|
||||
it('should emit refresh when tag updated successfully', async () => {
|
||||
spyOn(service.refresh, 'emit');
|
||||
spyOn(service.tagsApi, 'updateTag').and.returnValue(Promise.resolve(updatedTag));
|
||||
await service.updateTag(tag.entry.id, tagBody).toPromise();
|
||||
await firstValueFrom(service.updateTag(tag.entry.id, tagBody));
|
||||
expect(service.refresh.emit).toHaveBeenCalledWith(updatedTag);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { AdfHttpClient } from './adf-http-client.service';
|
||||
import { AlfrescoApiResponseError } from './alfresco-api/alfresco-api.response-error';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
|
||||
const securityOptions: SecurityOptions = {
|
||||
authentications: {},
|
||||
@@ -30,16 +31,9 @@ const securityOptions: SecurityOptions = {
|
||||
withCredentials: false
|
||||
};
|
||||
|
||||
const emitter = {
|
||||
emit: () => {},
|
||||
off: () => {},
|
||||
on: () => {},
|
||||
once: () => {}
|
||||
};
|
||||
|
||||
const emitters: Emitters = {
|
||||
eventEmitter: emitter,
|
||||
apiClientEmitter: emitter
|
||||
eventEmitter: new EventEmitter(),
|
||||
apiClientEmitter: new EventEmitter()
|
||||
};
|
||||
|
||||
const mockResponse = {
|
||||
@@ -124,10 +118,10 @@ describe('AdfHttpClient', () => {
|
||||
httpMethod: 'POST'
|
||||
};
|
||||
|
||||
const spy = spyOn(emitter, 'emit').and.callThrough();
|
||||
const eventSpy = spyOn(emitters.eventEmitter, 'emit').and.callThrough();
|
||||
|
||||
angularHttpClient.request('http://example.com', options, securityOptions, emitters).catch(() => {
|
||||
expect(spy).toHaveBeenCalledWith('unauthorized');
|
||||
expect(eventSpy).toHaveBeenCalledWith('unauthorized');
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { SHOULD_ADD_AUTH_TOKEN } from '@alfresco/adf-core/auth';
|
||||
import { Emitters as JsApiEmitters, HttpClient as JsApiHttpClient } from '@alfresco/js-api';
|
||||
import { HttpClient, HttpContext, HttpErrorResponse, HttpEvent, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, of, Subject, throwError } from 'rxjs';
|
||||
import { lastValueFrom, Observable, of, Subject, throwError } from 'rxjs';
|
||||
import { catchError, map, takeUntil } from 'rxjs/operators';
|
||||
import {
|
||||
convertObjectToFormData,
|
||||
@@ -34,23 +34,24 @@ import { AlfrescoApiParamEncoder } from './alfresco-api/alfresco-api.param-encod
|
||||
import { AlfrescoApiResponseError } from './alfresco-api/alfresco-api.response-error';
|
||||
import { Constructor } from './types';
|
||||
import { RequestOptions, SecurityOptions } from './interfaces';
|
||||
import ee, { Emitter } from 'event-emitter';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
|
||||
type EventEmitterInstance = InstanceType<typeof EventEmitter>;
|
||||
|
||||
type EventEmitterEvents = 'progress' | 'success' | 'error' | 'forbidden' | 'abort' | 'unauthorized' | string;
|
||||
|
||||
export interface Emitters {
|
||||
readonly eventEmitter: Emitter;
|
||||
readonly apiClientEmitter: Emitter;
|
||||
readonly eventEmitter: EventEmitterInstance;
|
||||
readonly apiClientEmitter: EventEmitterInstance;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AdfHttpClient implements ee.Emitter, JsApiHttpClient {
|
||||
on: ee.EmitterMethod;
|
||||
off: ee.EmitterMethod;
|
||||
once: ee.EmitterMethod;
|
||||
_disableCsrf: boolean;
|
||||
export class AdfHttpClient implements JsApiHttpClient {
|
||||
private eventEmitter = new EventEmitter();
|
||||
|
||||
emit: (type: string, ...args: any[]) => void;
|
||||
_disableCsrf: boolean;
|
||||
|
||||
get disableCsrf(): boolean {
|
||||
return this._disableCsrf;
|
||||
@@ -68,7 +69,27 @@ export class AdfHttpClient implements ee.Emitter, JsApiHttpClient {
|
||||
};
|
||||
|
||||
constructor(private httpClient: HttpClient) {
|
||||
ee(this);
|
||||
// No need for ee(this) anymore - we use composition instead of inheritance
|
||||
}
|
||||
|
||||
// EventEmitter delegation methods
|
||||
on(event: EventEmitterEvents, fn: (...args: any[]) => void, context?: any): this {
|
||||
this.eventEmitter.on(event, fn, context);
|
||||
return this;
|
||||
}
|
||||
|
||||
off(event: EventEmitterEvents, fn?: (...args: any[]) => void, context?: any): this {
|
||||
this.eventEmitter.off(event, fn, context);
|
||||
return this;
|
||||
}
|
||||
|
||||
once(event: EventEmitterEvents, fn: (...args: any[]) => void, context?: any): this {
|
||||
this.eventEmitter.once(event, fn, context);
|
||||
return this;
|
||||
}
|
||||
|
||||
emit(event: EventEmitterEvents, ...args: any[]): boolean {
|
||||
return this.eventEmitter.emit(event, ...args);
|
||||
}
|
||||
|
||||
setDefaultSecurityOption(options: any) {
|
||||
@@ -136,24 +157,19 @@ export class AdfHttpClient implements ee.Emitter, JsApiHttpClient {
|
||||
|
||||
private addPromiseListeners<T = any>(promise: Promise<T>, eventEmitter: any) {
|
||||
const eventPromise = Object.assign(promise, {
|
||||
on() {
|
||||
// eslint-disable-next-line prefer-spread, prefer-rest-params
|
||||
eventEmitter.on.apply(eventEmitter, arguments);
|
||||
on<K extends string | symbol>(event: K, fn: (...args: any[]) => void, context?: any) {
|
||||
eventEmitter.on(event, fn, context);
|
||||
return this;
|
||||
},
|
||||
once() {
|
||||
// eslint-disable-next-line prefer-spread, prefer-rest-params
|
||||
eventEmitter.once.apply(eventEmitter, arguments);
|
||||
once<K extends string | symbol>(event: K, fn: (...args: any[]) => void, context?: any) {
|
||||
eventEmitter.once(event, fn, context);
|
||||
return this;
|
||||
},
|
||||
emit() {
|
||||
// eslint-disable-next-line prefer-spread, prefer-rest-params
|
||||
eventEmitter.emit.apply(eventEmitter, arguments);
|
||||
return this;
|
||||
emit<K extends string | symbol>(event: K, ...args: any[]): boolean {
|
||||
return eventEmitter.emit(event, ...args);
|
||||
},
|
||||
off() {
|
||||
// eslint-disable-next-line prefer-spread, prefer-rest-params
|
||||
eventEmitter.off.apply(eventEmitter, arguments);
|
||||
off<K extends string | symbol>(event: K, fn?: (...args: any[]) => void, context?: any) {
|
||||
eventEmitter.off(event, fn, context);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
@@ -162,16 +178,17 @@ export class AdfHttpClient implements ee.Emitter, JsApiHttpClient {
|
||||
}
|
||||
|
||||
private getEventEmitters(): Emitters {
|
||||
const apiClientEmitter = {
|
||||
on: this.on.bind(this),
|
||||
off: this.off.bind(this),
|
||||
once: this.once.bind(this),
|
||||
emit: this.emit.bind(this)
|
||||
};
|
||||
const apiClientEmitter: EventEmitterInstance = new EventEmitter();
|
||||
|
||||
// Bind this instance's methods to the apiClientEmitter for backward compatibility
|
||||
apiClientEmitter.on = this.on.bind(this);
|
||||
apiClientEmitter.off = this.off.bind(this);
|
||||
apiClientEmitter.once = this.once.bind(this);
|
||||
apiClientEmitter.emit = this.emit.bind(this);
|
||||
|
||||
return {
|
||||
apiClientEmitter,
|
||||
eventEmitter: ee({})
|
||||
eventEmitter: new EventEmitter()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -179,8 +196,8 @@ export class AdfHttpClient implements ee.Emitter, JsApiHttpClient {
|
||||
const abort$ = new Subject<void>();
|
||||
const { eventEmitter, apiClientEmitter } = emitters;
|
||||
|
||||
const promise = request$
|
||||
.pipe(
|
||||
const promise = lastValueFrom(
|
||||
request$.pipe(
|
||||
map((res) => {
|
||||
if (isHttpUploadProgressEvent(res)) {
|
||||
const percent = Math.round((res.loaded / res.total) * 100);
|
||||
@@ -226,7 +243,7 @@ export class AdfHttpClient implements ee.Emitter, JsApiHttpClient {
|
||||
}),
|
||||
takeUntil(abort$)
|
||||
)
|
||||
.toPromise();
|
||||
);
|
||||
|
||||
(promise as any).abort = function () {
|
||||
eventEmitter.emit('abort');
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { isConstructor, getQueryParamsWithCustomEncoder, removeNilValues } from './alfresco-api.utils';
|
||||
import { isConstructor, getQueryParamsWithCustomEncoder, removeNilValues, convertObjectToFormData } from './alfresco-api.utils';
|
||||
|
||||
describe('AlfrescoApiUtils', () => {
|
||||
describe('isConstructor', () => {
|
||||
@@ -93,4 +93,30 @@ describe('AlfrescoApiUtils', () => {
|
||||
expect(actual?.getAll('key2')).toEqual(['value2', 'value3']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertObjectToFormData', () => {
|
||||
it('should create correct FormData entries for string values', () => {
|
||||
const testParams: Record<string, string> = { name: 'file name', description: 'file description' };
|
||||
const result = convertObjectToFormData(testParams);
|
||||
|
||||
expect(result.get('name')).toBe('file name');
|
||||
expect(result.get('description')).toBe('file description');
|
||||
});
|
||||
|
||||
it('should handle Blob files correctly', () => {
|
||||
const testFile = new File(['content'], 'test.txt', { type: 'text/plain' });
|
||||
const testParams: Record<string, Blob> = { file: testFile };
|
||||
|
||||
const result = convertObjectToFormData(testParams);
|
||||
expect(result.get('file')).toEqual(testFile);
|
||||
});
|
||||
|
||||
it('should create multiple entries with same key for arrays', () => {
|
||||
const testParams: Record<string, Array<string>> = { categories: ['category1', 'category2', 'category3'] };
|
||||
const result = convertObjectToFormData(testParams);
|
||||
|
||||
const values = result.getAll('categories');
|
||||
expect(values).toEqual(['category1', 'category2', 'category3']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -85,7 +85,7 @@ export const removeNilValues = (obj: Record<string | number, unknown>) => {
|
||||
}, {});
|
||||
};
|
||||
|
||||
export const convertObjectToFormData = (formParams: Record<string | number, string | Blob>): FormData => {
|
||||
export const convertObjectToFormData = (formParams: Record<string | number, string | Blob | Array<string | Blob>>): FormData => {
|
||||
const formData = new FormData();
|
||||
|
||||
for (const key in formParams) {
|
||||
@@ -93,6 +93,8 @@ export const convertObjectToFormData = (formParams: Record<string | number, stri
|
||||
const value = formParams[key];
|
||||
if (value instanceof File) {
|
||||
formData.append(key, value, value.name);
|
||||
} else if (Array.isArray(value)) {
|
||||
value.forEach((item) => formData.append(key, item));
|
||||
} else {
|
||||
formData.append(key, value);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@alfresco/adf-core",
|
||||
"description": "Alfresco ADF core",
|
||||
"version": "8.1.0",
|
||||
"version": "8.3.0",
|
||||
"author": "Hyland Software, Inc. and its affiliates",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -38,8 +38,8 @@
|
||||
"@angular/router": ">=16.0.0",
|
||||
"@mat-datetimepicker/core": ">=12.0.1",
|
||||
"@ngx-translate/core": ">=16.0.0",
|
||||
"@alfresco/js-api": ">=9.1.0",
|
||||
"@alfresco/adf-extensions": ">=8.1.0",
|
||||
"@alfresco/js-api": ">=9.1.1",
|
||||
"@alfresco/adf-extensions": ">=8.2.0",
|
||||
"minimatch": ">=10.0.0",
|
||||
"pdfjs-dist": ">=3.3.122",
|
||||
"ts-morph": ">=20.0.0"
|
||||
|
||||
@@ -127,22 +127,6 @@ describe('BasicAlfrescoAuthService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('isBpmLoggedIn should return value from processAuth', () => {
|
||||
spyOn(processAuth, 'isLoggedIn').and.returnValue(true);
|
||||
const result = basicAlfrescoAuthService.isBpmLoggedIn();
|
||||
|
||||
expect(result).toBeTrue();
|
||||
expect(processAuth.isLoggedIn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('isEcmLoggedIn should return value from contentAuth', () => {
|
||||
spyOn(contentAuth, 'isLoggedIn').and.returnValue(true);
|
||||
const result = basicAlfrescoAuthService.isEcmLoggedIn();
|
||||
|
||||
expect(result).toBeTrue();
|
||||
expect(contentAuth.isLoggedIn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('isLoggedIn', () => {
|
||||
let contentAuthSpy: jasmine.Spy;
|
||||
let processAuthSpy: jasmine.Spy;
|
||||
|
||||
@@ -43,7 +43,12 @@ export class BasicAlfrescoAuthService extends BaseAuthenticationService {
|
||||
type: 'basic'
|
||||
};
|
||||
|
||||
constructor(appConfig: AppConfigService, cookie: CookieService, private contentAuth: ContentAuth, private processAuth: ProcessAuth) {
|
||||
constructor(
|
||||
appConfig: AppConfigService,
|
||||
cookie: CookieService,
|
||||
private readonly contentAuth: ContentAuth,
|
||||
private readonly processAuth: ProcessAuth
|
||||
) {
|
||||
super(appConfig, cookie);
|
||||
|
||||
this.appConfig.onLoad.subscribe(() => {
|
||||
@@ -214,30 +219,6 @@ export class BasicAlfrescoAuthService extends BaseAuthenticationService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @returns content auth token
|
||||
*/
|
||||
getTicketEcm(): string {
|
||||
return this.contentAuth.getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @returns process auth token
|
||||
*/
|
||||
getTicketBpm(): string {
|
||||
return this.processAuth.getToken();
|
||||
}
|
||||
|
||||
isBpmLoggedIn(): boolean {
|
||||
return this.processAuth.isLoggedIn();
|
||||
}
|
||||
|
||||
isEcmLoggedIn(): boolean {
|
||||
return this.contentAuth.isLoggedIn();
|
||||
}
|
||||
|
||||
isLoggedIn(): boolean {
|
||||
const authWithCredentials = this.isKerberosEnabled();
|
||||
|
||||
@@ -317,22 +298,6 @@ export class BasicAlfrescoAuthService extends BaseAuthenticationService {
|
||||
return this.redirectUrl && (this.redirectUrl.provider === 'ALL' || provider === 'ALL');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `getUsername()` instead
|
||||
* @returns the username of the authenticated user
|
||||
*/
|
||||
getBpmUsername(): string {
|
||||
return this.processAuth.getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `getUsername()` instead
|
||||
* @returns the username of the authenticated user
|
||||
*/
|
||||
getEcmUsername(): string {
|
||||
return this.contentAuth.getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the username of the authenticated user.
|
||||
*
|
||||
|
||||
@@ -71,7 +71,7 @@ describe('AuthGuardService BPM', () => {
|
||||
});
|
||||
|
||||
it('should redirect url if the alfresco js api is NOT logged in and isOAuth with silentLogin', async () => {
|
||||
spyOn(authService, 'isBpmLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isOauth').and.returnValue(true);
|
||||
spyOn(oidcAuthenticationService, 'isPublicUrl').and.returnValue(false);
|
||||
spyOn(oidcAuthenticationService, 'ssoLogin').and.stub();
|
||||
@@ -101,7 +101,7 @@ describe('AuthGuardService BPM', () => {
|
||||
});
|
||||
|
||||
it('if the alfresco js api is configured with withCredentials true should canActivate be true', async () => {
|
||||
spyOn(authService, 'isBpmLoggedIn').and.returnValue(true);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(true);
|
||||
appConfigService.config.auth.withCredentials = true;
|
||||
|
||||
authGuard = TestBed.runInInjectionContext(() => AuthGuardBpm(route, state)) as Promise<boolean>;
|
||||
@@ -110,7 +110,7 @@ describe('AuthGuardService BPM', () => {
|
||||
});
|
||||
|
||||
it('if the alfresco js api is NOT logged in should canActivate be false', async () => {
|
||||
spyOn(authService, 'isBpmLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(false);
|
||||
|
||||
authGuard = TestBed.runInInjectionContext(() => AuthGuardBpm(route, state)) as Promise<boolean>;
|
||||
|
||||
@@ -119,7 +119,7 @@ describe('AuthGuardService BPM', () => {
|
||||
|
||||
it('if the alfresco js api is NOT logged in should trigger a redirect event', async () => {
|
||||
appConfigService.config.loginRoute = 'login';
|
||||
spyOn(authService, 'isBpmLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(false);
|
||||
|
||||
authGuard = TestBed.runInInjectionContext(() => AuthGuardBpm(route, state)) as Promise<boolean>;
|
||||
|
||||
@@ -128,7 +128,7 @@ describe('AuthGuardService BPM', () => {
|
||||
});
|
||||
|
||||
it('should redirect url if the alfresco js api is NOT logged in and isOAuthWithoutSilentLogin', async () => {
|
||||
spyOn(authService, 'isBpmLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isOauth').and.returnValue(true);
|
||||
appConfigService.config.oauth2.silentLogin = false;
|
||||
|
||||
@@ -139,7 +139,7 @@ describe('AuthGuardService BPM', () => {
|
||||
});
|
||||
|
||||
it('should redirect url if NOT logged in and isOAuth but no silentLogin configured', async () => {
|
||||
spyOn(authService, 'isBpmLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isOauth').and.returnValue(true);
|
||||
appConfigService.config.oauth2.silentLogin = undefined;
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ describe('AuthGuardService ECM', () => {
|
||||
});
|
||||
|
||||
it('if the alfresco js api is logged in should canActivate be true', async () => {
|
||||
spyOn(authService, 'isEcmLoggedIn').and.returnValue(true);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(true);
|
||||
|
||||
authGuard = TestBed.runInInjectionContext(() => AuthGuardEcm(route, state)) as Promise<boolean>;
|
||||
|
||||
@@ -79,7 +79,7 @@ describe('AuthGuardService ECM', () => {
|
||||
});
|
||||
|
||||
it('if the alfresco js api is configured with withCredentials true should canActivate be true', async () => {
|
||||
spyOn(authService, 'isBpmLoggedIn').and.returnValue(true);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(true);
|
||||
appConfigService.config.auth.withCredentials = true;
|
||||
|
||||
authGuard = TestBed.runInInjectionContext(() => AuthGuardEcm(route, state)) as Promise<boolean>;
|
||||
@@ -88,7 +88,7 @@ describe('AuthGuardService ECM', () => {
|
||||
});
|
||||
|
||||
it('if the alfresco js api is NOT logged in should canActivate be false', async () => {
|
||||
spyOn(authService, 'isEcmLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(false);
|
||||
|
||||
authGuard = TestBed.runInInjectionContext(() => AuthGuardEcm(route, state)) as Promise<boolean>;
|
||||
|
||||
@@ -97,7 +97,7 @@ describe('AuthGuardService ECM', () => {
|
||||
|
||||
it('if the alfresco js api is NOT logged in should trigger a redirect event', async () => {
|
||||
appConfigService.config.loginRoute = 'login';
|
||||
spyOn(authService, 'isEcmLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(false);
|
||||
|
||||
authGuard = TestBed.runInInjectionContext(() => AuthGuardEcm(route, state)) as Promise<boolean>;
|
||||
|
||||
@@ -106,7 +106,7 @@ describe('AuthGuardService ECM', () => {
|
||||
});
|
||||
|
||||
it('should redirect url if the alfresco js api is NOT logged in and isOAuthWithoutSilentLogin', async () => {
|
||||
spyOn(authService, 'isEcmLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isOauth').and.returnValue(true);
|
||||
appConfigService.config.oauth2.silentLogin = false;
|
||||
|
||||
@@ -117,7 +117,7 @@ describe('AuthGuardService ECM', () => {
|
||||
});
|
||||
|
||||
it('should redirect url if the alfresco js api is NOT logged in and isOAuth with silentLogin', async () => {
|
||||
spyOn(authService, 'isEcmLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isOauth').and.returnValue(true);
|
||||
spyOn(oidcAuthenticationService, 'isPublicUrl').and.returnValue(false);
|
||||
spyOn(oidcAuthenticationService, 'ssoLogin').and.stub();
|
||||
@@ -139,7 +139,7 @@ describe('AuthGuardService ECM', () => {
|
||||
});
|
||||
|
||||
it('should not redirect url if NOT logged in and isOAuth but no silentLogin configured', async () => {
|
||||
spyOn(authService, 'isEcmLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(false);
|
||||
spyOn(authService, 'isOauth').and.returnValue(true);
|
||||
appConfigService.config.oauth2.silentLogin = undefined;
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export const AuthGuardEcm: CanActivateFn = async (_: ActivatedRouteSnapshot, sta
|
||||
return authGuardBaseService.redirectSSOSuccessURL();
|
||||
}
|
||||
|
||||
if (authenticationService.isEcmLoggedIn() || authGuardBaseService.withCredentials) {
|
||||
if (authenticationService.isLoggedIn() || authGuardBaseService.withCredentials) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ describe('AuthGuardService', () => {
|
||||
});
|
||||
|
||||
it('if the alfresco js api is configured with withCredentials true should canActivate be true', async () => {
|
||||
spyOn(authService, 'isBpmLoggedIn').and.returnValue(true);
|
||||
spyOn(authService, 'isLoggedIn').and.returnValue(true);
|
||||
appConfigService.config.auth.withCredentials = true;
|
||||
|
||||
authGuard = TestBed.runInInjectionContext(() => AuthGuard(route, state)) as Promise<boolean>;
|
||||
|
||||
@@ -16,18 +16,20 @@
|
||||
*/
|
||||
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
import ee from 'event-emitter';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
type EventEmitterInstance = InstanceType<typeof EventEmitter>;
|
||||
|
||||
export interface AuthenticationServiceInterface {
|
||||
onError: any;
|
||||
onLogin: any;
|
||||
onLogout: any;
|
||||
|
||||
on: ee.EmitterMethod;
|
||||
off: ee.EmitterMethod;
|
||||
once: ee.EmitterMethod;
|
||||
emit: (type: string, ...args: any[]) => void;
|
||||
on: EventEmitterInstance['on'];
|
||||
off: EventEmitterInstance['off'];
|
||||
once: EventEmitterInstance['once'];
|
||||
emit: EventEmitterInstance['emit'];
|
||||
|
||||
getToken(): string;
|
||||
isLoggedIn(): boolean;
|
||||
@@ -42,14 +44,4 @@ export interface AuthenticationServiceInterface {
|
||||
getAuthHeaders(requestUrl: string, header: HttpHeaders): HttpHeaders;
|
||||
addTokenToHeader(requestUrl: string, headersArg?: HttpHeaders): Observable<HttpHeaders>;
|
||||
reset(): void;
|
||||
|
||||
/** @deprecated use `isLoggedIn` instead, use `isECMProvider` if you need to know the auth type */
|
||||
isEcmLoggedIn(): boolean;
|
||||
/** @deprecated use `isLoggedIn` instead, use `isBPMProvider` if you need to know the auth type */
|
||||
isBpmLoggedIn(): boolean;
|
||||
|
||||
/** @deprecated use `getUsername` instead */
|
||||
getEcmUsername(): string;
|
||||
/** @deprecated use `getUsername` instead */
|
||||
getBpmUsername(): string;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
IdentityJoinGroupRequestModel
|
||||
} from '../interfaces/identity-user.service.interface';
|
||||
import { mockIdentityGroups } from './identity-group.mock';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { firstValueFrom, Observable, of } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { mockAssignedRoles, mockAvailableRoles, mockEffectiveRoles, mockIdentityUser1, mockIdentityUsers } from './identity-user.mock';
|
||||
|
||||
@@ -127,7 +127,7 @@ export class IdentityUserServiceMock implements IdentityUserServiceInterface {
|
||||
async getUsersByRolesWithCurrentUser(roleNames: string[]): Promise<IdentityUserModel[]> {
|
||||
const filteredUsers: IdentityUserModel[] = [];
|
||||
if (roleNames && roleNames.length > 0) {
|
||||
const users = await this.getUsers().toPromise();
|
||||
const users = await firstValueFrom(this.getUsers());
|
||||
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
const hasAnyRole = await this.userHasAnyRole(users[i].id, roleNames);
|
||||
@@ -144,7 +144,7 @@ export class IdentityUserServiceMock implements IdentityUserServiceInterface {
|
||||
const filteredUsers: IdentityUserModel[] = [];
|
||||
if (roleNames && roleNames.length > 0) {
|
||||
const currentUser = this.getCurrentUserInfo();
|
||||
let users = await this.getUsers().toPromise();
|
||||
let users = await firstValueFrom(this.getUsers());
|
||||
|
||||
users = users.filter(({ username }) => username !== currentUser.username);
|
||||
|
||||
@@ -160,7 +160,7 @@ export class IdentityUserServiceMock implements IdentityUserServiceInterface {
|
||||
}
|
||||
|
||||
private async userHasAnyRole(userId: string, roleNames: string[]): Promise<boolean> {
|
||||
const userRoles = await this.getUserRoles(userId).toPromise();
|
||||
const userRoles = await firstValueFrom(this.getUserRoles(userId));
|
||||
const hasAnyRole = roleNames.some((roleName) => {
|
||||
const filteredRoles = userRoles.filter((userRole) => userRole.name.toLocaleLowerCase() === roleName.toLocaleLowerCase());
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { EMPTY } from 'rxjs';
|
||||
import { of } from 'rxjs';
|
||||
import { AppConfigService } from '../../app-config/app-config.service';
|
||||
import { AUTH_MODULE_CONFIG } from './auth-config';
|
||||
import { AuthConfigService } from './auth-config.service';
|
||||
@@ -102,7 +102,7 @@ describe('AuthConfigService', () => {
|
||||
spyOn<any>(service, 'getLocationOrigin').and.returnValue('http://localhost:3000');
|
||||
|
||||
appConfigService = TestBed.inject(AppConfigService);
|
||||
appConfigService.onLoad = EMPTY;
|
||||
appConfigService.onLoad = of(true);
|
||||
});
|
||||
|
||||
describe('load auth config using hash', () => {
|
||||
|
||||
@@ -21,6 +21,7 @@ import { take } from 'rxjs/operators';
|
||||
import { AppConfigService } from '../../app-config/app-config.service';
|
||||
import { AUTH_MODULE_CONFIG, AuthModuleConfig } from './auth-config';
|
||||
import { OauthConfigModel } from '../models/oauth-config.model';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Create auth configuration factory
|
||||
@@ -36,7 +37,10 @@ export function authConfigFactory(authConfigService: AuthConfigService): Promise
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthConfigService {
|
||||
constructor(private appConfigService: AppConfigService, @Inject(AUTH_MODULE_CONFIG) private readonly authModuleConfig: AuthModuleConfig) {}
|
||||
constructor(
|
||||
private appConfigService: AppConfigService,
|
||||
@Inject(AUTH_MODULE_CONFIG) private readonly authModuleConfig: AuthModuleConfig
|
||||
) {}
|
||||
|
||||
private _authConfig!: AuthConfig;
|
||||
get authConfig(): AuthConfig {
|
||||
@@ -44,7 +48,7 @@ export class AuthConfigService {
|
||||
}
|
||||
|
||||
loadConfig(): Promise<AuthConfig> {
|
||||
return this.appConfigService.onLoad.pipe(take(1)).toPromise().then(this.loadAppConfig.bind(this));
|
||||
return firstValueFrom(this.appConfigService.onLoad.pipe(take(1))).then(this.loadAppConfig.bind(this));
|
||||
}
|
||||
|
||||
loadAppConfig(): AuthConfig {
|
||||
|
||||
@@ -66,7 +66,6 @@ const mockAuthService = {
|
||||
describe('OidcAuthenticationService', () => {
|
||||
let service: OidcAuthenticationService;
|
||||
let oauthService: OAuthService;
|
||||
let appConfig: AppConfigService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -81,7 +80,6 @@ describe('OidcAuthenticationService', () => {
|
||||
});
|
||||
service = TestBed.inject(OidcAuthenticationService);
|
||||
oauthService = TestBed.inject(OAuthService);
|
||||
appConfig = TestBed.inject(AppConfigService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
@@ -189,60 +187,6 @@ describe('OidcAuthenticationService', () => {
|
||||
expect(service.isLoggedIn()).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isEcmLoggedIn', () => {
|
||||
beforeEach(() => {
|
||||
mockOAuthService.hasValidAccessToken.and.returnValue(true);
|
||||
mockOAuthService.hasValidIdToken.and.returnValue(true);
|
||||
});
|
||||
|
||||
it('should return true if is ECM provider', () => {
|
||||
spyOn(appConfig, 'get').and.returnValue('ECM');
|
||||
expect(service.isEcmLoggedIn()).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return true if is all provider', () => {
|
||||
spyOn(appConfig, 'get').and.returnValue('ALL');
|
||||
expect(service.isEcmLoggedIn()).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return false if is not ECM provider', () => {
|
||||
spyOn(appConfig, 'get').and.returnValue('BPM');
|
||||
expect(service.isEcmLoggedIn()).toBeFalse();
|
||||
});
|
||||
|
||||
it('should return false if provider is not defined', () => {
|
||||
spyOn(appConfig, 'get').and.returnValue(undefined);
|
||||
expect(service.isEcmLoggedIn()).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isBpmLoggedIn', () => {
|
||||
beforeEach(() => {
|
||||
mockOAuthService.hasValidAccessToken.and.returnValue(true);
|
||||
mockOAuthService.hasValidIdToken.and.returnValue(true);
|
||||
});
|
||||
|
||||
it('should return true if is BPM provider', () => {
|
||||
spyOn(appConfig, 'get').and.returnValue('BPM');
|
||||
expect(service.isBpmLoggedIn()).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return true if is all provider', () => {
|
||||
spyOn(appConfig, 'get').and.returnValue('ALL');
|
||||
expect(service.isBpmLoggedIn()).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return false if is not BPM provider', () => {
|
||||
spyOn(appConfig, 'get').and.returnValue('ECM');
|
||||
expect(service.isBpmLoggedIn()).toBeFalse();
|
||||
});
|
||||
|
||||
it('should return false if provider is not defined', () => {
|
||||
spyOn(appConfig, 'get').and.returnValue(undefined);
|
||||
expect(service.isBpmLoggedIn()).toBeFalse();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('OidcAuthenticationService shouldPerformSsoLogin', () => {
|
||||
@@ -270,7 +214,7 @@ describe('OidcAuthenticationService shouldPerformSsoLogin', () => {
|
||||
};
|
||||
configureTestingModule({ provide: AuthService, useValue: mockAuthServiceValue });
|
||||
|
||||
const shouldPerformSsoLogin = await service.shouldPerformSsoLogin$.toPromise();
|
||||
const shouldPerformSsoLogin = await firstValueFrom(service.shouldPerformSsoLogin$);
|
||||
expect(shouldPerformSsoLogin).toBeTrue();
|
||||
});
|
||||
|
||||
@@ -281,7 +225,7 @@ describe('OidcAuthenticationService shouldPerformSsoLogin', () => {
|
||||
};
|
||||
configureTestingModule({ provide: AuthService, useValue: mockAuthServiceValue });
|
||||
|
||||
const shouldPerformSsoLogin = await service.shouldPerformSsoLogin$.toPromise();
|
||||
const shouldPerformSsoLogin = await firstValueFrom(service.shouldPerformSsoLogin$);
|
||||
expect(shouldPerformSsoLogin).toBeFalse();
|
||||
});
|
||||
|
||||
@@ -292,7 +236,7 @@ describe('OidcAuthenticationService shouldPerformSsoLogin', () => {
|
||||
};
|
||||
configureTestingModule({ provide: AuthService, useValue: mockAuthServiceValue });
|
||||
|
||||
const shouldPerformSsoLogin = await service.shouldPerformSsoLogin$.toPromise();
|
||||
const shouldPerformSsoLogin = await firstValueFrom(service.shouldPerformSsoLogin$);
|
||||
expect(shouldPerformSsoLogin).toBeFalse();
|
||||
});
|
||||
|
||||
@@ -303,7 +247,7 @@ describe('OidcAuthenticationService shouldPerformSsoLogin', () => {
|
||||
};
|
||||
configureTestingModule({ provide: AuthService, useValue: mockAuthServiceValue });
|
||||
|
||||
const shouldPerformSsoLogin = await service.shouldPerformSsoLogin$.toPromise();
|
||||
const shouldPerformSsoLogin = await firstValueFrom(service.shouldPerformSsoLogin$);
|
||||
expect(shouldPerformSsoLogin).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -56,28 +56,6 @@ export class OidcAuthenticationService extends BaseAuthenticationService {
|
||||
map(([authenticated, isDiscoveryDocumentLoaded]) => !authenticated && isDiscoveryDocumentLoaded)
|
||||
);
|
||||
|
||||
/**
|
||||
* @deprecated use `isLoggedIn` instead
|
||||
* @returns true if the ECM provider is logged in
|
||||
*/
|
||||
isEcmLoggedIn(): boolean {
|
||||
if (this.isECMProvider() || this.isALLProvider()) {
|
||||
return this.isLoggedIn();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `isLoggedIn` instead
|
||||
* @returns true if the BPM provider is logged in
|
||||
*/
|
||||
isBpmLoggedIn(): boolean {
|
||||
if (this.isBPMProvider() || this.isALLProvider()) {
|
||||
return this.isLoggedIn();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isLoggedIn(): boolean {
|
||||
return this.oauthService.hasValidAccessToken() && this.oauthService.hasValidIdToken();
|
||||
}
|
||||
@@ -132,22 +110,6 @@ export class OidcAuthenticationService extends BaseAuthenticationService {
|
||||
return this.jwtHelperService.getValueFromLocalToken<string>(JwtHelperService.USER_PREFERRED_USERNAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `getUsername` instead
|
||||
* @returns the logged username
|
||||
*/
|
||||
getEcmUsername(): string {
|
||||
return this.getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `getUsername` instead
|
||||
* @returns the logged username
|
||||
*/
|
||||
getBpmUsername(): string {
|
||||
return this.getUsername();
|
||||
}
|
||||
|
||||
ssoLogin(redirectUrl?: string) {
|
||||
this.auth.login(redirectUrl);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
OAuthSuccessEvent,
|
||||
OAuthInfoEvent
|
||||
} from 'angular-oauth2-oidc';
|
||||
import { of, Subject, timeout } from 'rxjs';
|
||||
import { firstValueFrom, of, Subject, timeout } from 'rxjs';
|
||||
import { RedirectAuthService } from './redirect-auth.service';
|
||||
import { AUTH_MODULE_CONFIG } from './auth-config';
|
||||
import { RetryLoginService } from './retry-login.service';
|
||||
@@ -389,8 +389,8 @@ describe('RedirectAuthService', () => {
|
||||
it('should NOT logout user if the refresh token failed first time', fakeAsync(async () => {
|
||||
const expectedFakeErrorEvent = new OAuthErrorEvent('token_refresh_error', { reason: 'error' }, {});
|
||||
|
||||
const firstEventOccurPromise = service.firstOauthErrorEventOccur$.toPromise();
|
||||
const secondTokenRefreshErrorEventPromise = service.secondTokenRefreshErrorEventOccur$.pipe(timeout(1000)).toPromise();
|
||||
const firstEventOccurPromise = firstValueFrom(service.firstOauthErrorEventOccur$);
|
||||
const secondTokenRefreshErrorEventPromise = firstValueFrom(service.secondTokenRefreshErrorEventOccur$.pipe(timeout(1000)));
|
||||
|
||||
oauthEvents$.next(new OAuthErrorEvent('token_refresh_error', { reason: 'error' }, {}));
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ describe('AuthenticationService', () => {
|
||||
spyOn(authService, 'isECMProvider').and.returnValue(true);
|
||||
spyOn(authService, 'isOauth').and.returnValue(false);
|
||||
|
||||
expect(authService.isEcmLoggedIn()).toBeFalsy();
|
||||
expect(authService.isLoggedIn()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should require remember me set for ECM check', () => {
|
||||
@@ -149,7 +149,7 @@ describe('AuthenticationService', () => {
|
||||
spyOn(authService, 'isECMProvider').and.returnValue(true);
|
||||
spyOn(authService, 'isOauth').and.returnValue(false);
|
||||
|
||||
expect(authService.isEcmLoggedIn()).toBeFalsy();
|
||||
expect(authService.isLoggedIn()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('[ECM] should login in the ECM if no provider are defined calling the login', async () => {
|
||||
@@ -167,7 +167,6 @@ describe('AuthenticationService', () => {
|
||||
|
||||
it('[ECM] should return false if the user is not logged in', () => {
|
||||
expect(authService.isLoggedIn()).toBe(false);
|
||||
expect(authService.isEcmLoggedIn()).toBe(false);
|
||||
});
|
||||
|
||||
it('[ECM] should set/get redirectUrl when provider is ECM', () => {
|
||||
@@ -199,10 +198,6 @@ describe('AuthenticationService', () => {
|
||||
it('[ECM] should return isALLProvider false', () => {
|
||||
expect(authService.isALLProvider()).toBe(false);
|
||||
});
|
||||
|
||||
it('[ECM] should return isBpmLoggedIn false', () => {
|
||||
expect(authService.isBpmLoggedIn()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the setting is BPM', () => {
|
||||
@@ -217,7 +212,7 @@ describe('AuthenticationService', () => {
|
||||
spyOn(authService, 'isBPMProvider').and.returnValue(true);
|
||||
spyOn(authService, 'isOauth').and.returnValue(false);
|
||||
|
||||
expect(authService.isBpmLoggedIn()).toBeFalsy();
|
||||
expect(authService.isLoggedIn()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not require cookie service enabled for BPM check', () => {
|
||||
@@ -225,7 +220,7 @@ describe('AuthenticationService', () => {
|
||||
spyOn(basicAlfrescoAuthService, 'isRememberMeSet').and.returnValue(false);
|
||||
spyOn(authService, 'isBPMProvider').and.returnValue(true);
|
||||
|
||||
expect(authService.isBpmLoggedIn()).toBeFalsy();
|
||||
expect(authService.isLoggedIn()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('[BPM] should return an error when the logout return error', async () => {
|
||||
|
||||
@@ -21,18 +21,21 @@ import { BasicAlfrescoAuthService } from '../basic-auth/basic-alfresco-auth.serv
|
||||
import { Observable, Subject, from } from 'rxjs';
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
import { AuthenticationServiceInterface } from '../interfaces/authentication-service.interface';
|
||||
import ee from 'event-emitter';
|
||||
import { RedirectAuthService } from '../oidc/redirect-auth.service';
|
||||
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
type EventEmitterInstance = InstanceType<typeof EventEmitter>;
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthenticationService implements AuthenticationServiceInterface, ee.Emitter {
|
||||
export class AuthenticationService implements AuthenticationServiceInterface {
|
||||
onLogin: Subject<any> = new Subject<any>();
|
||||
onLogout: Subject<any> = new Subject<any>();
|
||||
onTokenReceived: Subject<any> = new Subject<any>();
|
||||
|
||||
constructor(private injector: Injector, private redirectAuthService: RedirectAuthService) {
|
||||
constructor(
|
||||
private readonly injector: Injector,
|
||||
private readonly redirectAuthService: RedirectAuthService
|
||||
) {
|
||||
this.redirectAuthService.onLogin.subscribe((value) => this.onLogin.next(value));
|
||||
|
||||
this.redirectAuthService.onTokenReceived.subscribe((value) => this.onTokenReceived.next(value));
|
||||
@@ -46,19 +49,19 @@ export class AuthenticationService implements AuthenticationServiceInterface, ee
|
||||
}
|
||||
}
|
||||
|
||||
get on(): ee.EmitterMethod {
|
||||
get on(): EventEmitterInstance['on'] {
|
||||
return this.isOauth() ? this.oidcAuthenticationService.on : this.basicAlfrescoAuthService.on;
|
||||
}
|
||||
|
||||
get off(): ee.EmitterMethod {
|
||||
get off(): EventEmitterInstance['off'] {
|
||||
return this.isOauth() ? this.oidcAuthenticationService.off : this.basicAlfrescoAuthService.off;
|
||||
}
|
||||
|
||||
get once(): ee.EmitterMethod {
|
||||
get once(): EventEmitterInstance['once'] {
|
||||
return this.isOauth() ? this.oidcAuthenticationService.once : this.basicAlfrescoAuthService.once;
|
||||
}
|
||||
|
||||
get emit(): (type: string, ...args: any[]) => void {
|
||||
get emit(): EventEmitterInstance['emit'] {
|
||||
return this.isOauth() ? this.oidcAuthenticationService.emit : this.basicAlfrescoAuthService.emit;
|
||||
}
|
||||
|
||||
@@ -116,30 +119,6 @@ export class AuthenticationService implements AuthenticationServiceInterface, ee
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `isLoggedIn` instead
|
||||
* @returns true if the ECM provider is logged in
|
||||
*/
|
||||
isEcmLoggedIn(): boolean {
|
||||
if (this.isOauth()) {
|
||||
return this.oidcAuthenticationService.isLoggedIn();
|
||||
} else {
|
||||
return this.basicAlfrescoAuthService.isEcmLoggedIn();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `isLoggedIn` instead
|
||||
* @returns true if the BPM provider is logged in
|
||||
*/
|
||||
isBpmLoggedIn(): boolean {
|
||||
if (this.isOauth()) {
|
||||
return this.oidcAuthenticationService.isLoggedIn();
|
||||
} else {
|
||||
return this.basicAlfrescoAuthService.isBpmLoggedIn();
|
||||
}
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
if (this.isOauth()) {
|
||||
return this.oidcAuthenticationService.reset();
|
||||
@@ -169,22 +148,6 @@ export class AuthenticationService implements AuthenticationServiceInterface, ee
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `getUsername` instead
|
||||
* @returns the logged username
|
||||
*/
|
||||
getEcmUsername(): string {
|
||||
return this.getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `getUsername` instead
|
||||
* @returns the logged username
|
||||
*/
|
||||
getBpmUsername(): string {
|
||||
return this.getUsername();
|
||||
}
|
||||
|
||||
getAuthHeaders(requestUrl: string, headers: HttpHeaders): HttpHeaders {
|
||||
if (this.isOauth()) {
|
||||
return this.oidcAuthenticationService.getAuthHeaders(requestUrl, headers);
|
||||
|
||||
@@ -21,13 +21,15 @@ import { Observable, Observer, ReplaySubject, throwError } from 'rxjs';
|
||||
import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service';
|
||||
import { CookieService } from '../../common/services/cookie.service';
|
||||
import { AuthenticationServiceInterface } from '../interfaces/authentication-service.interface';
|
||||
import ee from 'event-emitter';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
|
||||
export abstract class BaseAuthenticationService implements AuthenticationServiceInterface, ee.Emitter {
|
||||
on: ee.EmitterMethod;
|
||||
off: ee.EmitterMethod;
|
||||
once: ee.EmitterMethod;
|
||||
emit: (type: string, ...args: any[]) => void;
|
||||
type EventEmitterInstance = InstanceType<typeof EventEmitter>;
|
||||
|
||||
export abstract class BaseAuthenticationService implements AuthenticationServiceInterface {
|
||||
on: EventEmitterInstance['on'];
|
||||
off: EventEmitterInstance['off'];
|
||||
once: EventEmitterInstance['once'];
|
||||
emit: EventEmitterInstance['emit'];
|
||||
|
||||
protected redirectUrl: RedirectionModel = null;
|
||||
|
||||
@@ -35,30 +37,18 @@ export abstract class BaseAuthenticationService implements AuthenticationService
|
||||
onLogin = new ReplaySubject<any>(1);
|
||||
onLogout = new ReplaySubject<any>(1);
|
||||
|
||||
protected constructor(protected appConfig: AppConfigService, protected cookie: CookieService) {
|
||||
ee(this);
|
||||
}
|
||||
protected constructor(
|
||||
protected appConfig: AppConfigService,
|
||||
protected cookie: CookieService
|
||||
) {}
|
||||
|
||||
abstract getAuthHeaders(requestUrl: string, header: HttpHeaders): HttpHeaders;
|
||||
abstract getToken(): string;
|
||||
abstract isLoggedIn(): boolean;
|
||||
abstract logout(): any;
|
||||
|
||||
/** @deprecated use `isLoggedIn` instead */
|
||||
abstract isEcmLoggedIn(): boolean;
|
||||
|
||||
/** @deprecated use `isLoggedIn` instead */
|
||||
abstract isBpmLoggedIn(): boolean;
|
||||
|
||||
abstract reset(): void;
|
||||
abstract getUsername(): string;
|
||||
|
||||
/** @deprecated use `getUsername` instead */
|
||||
abstract getEcmUsername(): string;
|
||||
|
||||
/** @deprecated use `getUsername` instead */
|
||||
abstract getBpmUsername(): string;
|
||||
|
||||
/**
|
||||
* Adds the auth token to an HTTP header using the 'bearer' scheme.
|
||||
*
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { firstValueFrom, Observable, of } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { AppConfigService } from '../../app-config/app-config.service';
|
||||
import { IdentityGroupModel } from '../models/identity-group.model';
|
||||
@@ -36,7 +36,11 @@ import { OAuth2Service } from './oauth2.service';
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class IdentityUserService implements IdentityUserServiceInterface {
|
||||
constructor(private jwtHelperService: JwtHelperService, private oAuth2Service: OAuth2Service, private appConfigService: AppConfigService) {}
|
||||
constructor(
|
||||
private jwtHelperService: JwtHelperService,
|
||||
private oAuth2Service: OAuth2Service,
|
||||
private appConfigService: AppConfigService
|
||||
) {}
|
||||
|
||||
private get identityHost(): string {
|
||||
return `${this.appConfigService.get('identityHost')}`;
|
||||
@@ -239,7 +243,7 @@ export class IdentityUserService implements IdentityUserServiceInterface {
|
||||
async getUsersByRolesWithCurrentUser(roleNames: string[]): Promise<IdentityUserModel[]> {
|
||||
const filteredUsers: IdentityUserModel[] = [];
|
||||
if (roleNames && roleNames.length > 0) {
|
||||
const users = await this.getUsers().toPromise();
|
||||
const users = await firstValueFrom(this.getUsers());
|
||||
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
const hasAnyRole = await this.userHasAnyRole(users[i].id, roleNames);
|
||||
@@ -262,7 +266,7 @@ export class IdentityUserService implements IdentityUserServiceInterface {
|
||||
const filteredUsers: IdentityUserModel[] = [];
|
||||
if (roleNames && roleNames.length > 0) {
|
||||
const currentUser = this.getCurrentUserInfo();
|
||||
let users = await this.getUsers().toPromise();
|
||||
let users = await firstValueFrom(this.getUsers());
|
||||
|
||||
users = users.filter(({ username }) => username !== currentUser.username);
|
||||
|
||||
@@ -278,7 +282,7 @@ export class IdentityUserService implements IdentityUserServiceInterface {
|
||||
}
|
||||
|
||||
private async userHasAnyRole(userId: string, roleNames: string[]): Promise<boolean> {
|
||||
const userRoles = await this.getUserRoles(userId).toPromise();
|
||||
const userRoles = await firstValueFrom(this.getUserRoles(userId));
|
||||
const hasAnyRole = roleNames.some((roleName) => {
|
||||
const filteredRoles = userRoles.filter((userRole) => userRole.name.toLocaleLowerCase() === roleName.toLocaleLowerCase());
|
||||
|
||||
@@ -337,7 +341,7 @@ export class IdentityUserService implements IdentityUserServiceInterface {
|
||||
hasMoreItems: false,
|
||||
totalItems: totalCount
|
||||
}
|
||||
} as IdentityUserQueryResponse)
|
||||
}) as IdentityUserQueryResponse
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -19,6 +19,7 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { AppConfigService } from '../../app-config/app-config.service';
|
||||
import { TimeSyncService } from './time-sync.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
describe('TimeSyncService', () => {
|
||||
let service: TimeSyncService;
|
||||
@@ -30,10 +31,7 @@ describe('TimeSyncService', () => {
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule],
|
||||
providers: [
|
||||
TimeSyncService,
|
||||
{ provide: AppConfigService, useValue: appConfigSpy }
|
||||
]
|
||||
providers: [TimeSyncService, { provide: AppConfigService, useValue: appConfigSpy }]
|
||||
});
|
||||
|
||||
service = TestBed.inject(TimeSyncService);
|
||||
@@ -45,7 +43,6 @@ describe('TimeSyncService', () => {
|
||||
});
|
||||
|
||||
describe('checkTimeSync', () => {
|
||||
|
||||
it('should check time sync and return outOfSync as false when time is within allowed skew', () => {
|
||||
appConfigSpy.get.and.returnValue('http://fake-server-time-url');
|
||||
|
||||
@@ -58,15 +55,11 @@ describe('TimeSyncService', () => {
|
||||
|
||||
const serverTime = 1728911640000; // (GMT): Monday, October 14, 2024 1:14:00 PM
|
||||
|
||||
spyOn(Date, 'now').and.returnValues(
|
||||
timeBeforeCallingServerTimeEndpoint,
|
||||
timeResponseReceivedFromServerTimeEndpoint,
|
||||
localCurrentTime
|
||||
);
|
||||
spyOn(Date, 'now').and.returnValues(timeBeforeCallingServerTimeEndpoint, timeResponseReceivedFromServerTimeEndpoint, localCurrentTime);
|
||||
|
||||
// difference between localCurrentTime and serverTime is 60 seconds plus the round trip time of 1 second
|
||||
const allowedClockSkewInSec = 61;
|
||||
service.checkTimeSync(allowedClockSkewInSec).subscribe(sync => {
|
||||
service.checkTimeSync(allowedClockSkewInSec).subscribe((sync) => {
|
||||
expect(sync.outOfSync).toBeFalse();
|
||||
expect(sync.localDateTimeISO).toEqual('2024-10-14T13:13:00.000Z');
|
||||
expect(sync.serverDateTimeISO).toEqual('2024-10-14T13:14:00.500Z');
|
||||
@@ -89,16 +82,12 @@ describe('TimeSyncService', () => {
|
||||
|
||||
const serverTime = 1728911640000; // (GMT): Monday, October 14, 2024 1:14:00 PM
|
||||
|
||||
spyOn(Date, 'now').and.returnValues(
|
||||
timeBeforeCallingServerTimeEndpoint,
|
||||
timeResponseReceivedFromServerTimeEndpoint,
|
||||
localCurrentTime
|
||||
);
|
||||
spyOn(Date, 'now').and.returnValues(timeBeforeCallingServerTimeEndpoint, timeResponseReceivedFromServerTimeEndpoint, localCurrentTime);
|
||||
|
||||
// difference between localCurrentTime and serverTime is 60 seconds plus the round trip time of 1 second
|
||||
// setting allowedClockSkewInSec to 60 seconds will make the local time out of sync
|
||||
const allowedClockSkewInSec = 60;
|
||||
service.checkTimeSync(allowedClockSkewInSec).subscribe(sync => {
|
||||
service.checkTimeSync(allowedClockSkewInSec).subscribe((sync) => {
|
||||
expect(sync.outOfSync).toBeTrue();
|
||||
expect(sync.localDateTimeISO).toEqual('2024-10-14T13:13:00.000Z');
|
||||
expect(sync.serverDateTimeISO).toEqual('2024-10-14T13:14:00.500Z');
|
||||
@@ -113,12 +102,11 @@ describe('TimeSyncService', () => {
|
||||
appConfigSpy.get.and.returnValue('');
|
||||
|
||||
try {
|
||||
await service.checkTimeSync(60).toPromise();
|
||||
await firstValueFrom(service.checkTimeSync(60));
|
||||
fail('Expected to throw an error');
|
||||
} catch (error) {
|
||||
expect(error.message).toBe('serverTimeUrl is not configured.');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it('should throw an error if the server time endpoint returns an error', () => {
|
||||
@@ -130,7 +118,7 @@ describe('TimeSyncService', () => {
|
||||
next: () => {
|
||||
fail('Expected to throw an error');
|
||||
},
|
||||
error: error => {
|
||||
error: (error) => {
|
||||
expect(error.message).toBe('Error: Failed to get server time');
|
||||
}
|
||||
});
|
||||
@@ -139,12 +127,10 @@ describe('TimeSyncService', () => {
|
||||
expect(req.request.method).toBe('GET');
|
||||
req.error(new ProgressEvent(''));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('isLocalTimeOutOfSync', () => {
|
||||
it('should return clock is out of sync', () => {
|
||||
|
||||
appConfigSpy.get.and.returnValue('http://fake-server-time-url');
|
||||
|
||||
const expectedServerTimeUrl = 'http://fake-server-time-url';
|
||||
@@ -156,16 +142,12 @@ describe('TimeSyncService', () => {
|
||||
|
||||
const serverTime = 1728911640000; // (GMT): Monday, October 14, 2024 1:14:00 PM
|
||||
|
||||
spyOn(Date, 'now').and.returnValues(
|
||||
timeBeforeCallingServerTimeEndpoint,
|
||||
timeResponseReceivedFromServerTimeEndpoint,
|
||||
localCurrentTime
|
||||
);
|
||||
spyOn(Date, 'now').and.returnValues(timeBeforeCallingServerTimeEndpoint, timeResponseReceivedFromServerTimeEndpoint, localCurrentTime);
|
||||
|
||||
// difference between localCurrentTime and serverTime is 60 seconds plus the round trip time of 1 second
|
||||
// setting allowedClockSkewInSec to 60 seconds will make the local time out of sync
|
||||
const allowedClockSkewInSec = 60;
|
||||
service.isLocalTimeOutOfSync(allowedClockSkewInSec).subscribe(isOutOfSync => {
|
||||
service.isLocalTimeOutOfSync(allowedClockSkewInSec).subscribe((isOutOfSync) => {
|
||||
expect(isOutOfSync).toBeTrue();
|
||||
});
|
||||
|
||||
@@ -186,15 +168,11 @@ describe('TimeSyncService', () => {
|
||||
|
||||
const serverTime = 1728911640000; // (GMT): Monday, October 14, 2024 1:14:00 PM
|
||||
|
||||
spyOn(Date, 'now').and.returnValues(
|
||||
timeBeforeCallingServerTimeEndpoint,
|
||||
timeResponseReceivedFromServerTimeEndpoint,
|
||||
localCurrentTime
|
||||
);
|
||||
spyOn(Date, 'now').and.returnValues(timeBeforeCallingServerTimeEndpoint, timeResponseReceivedFromServerTimeEndpoint, localCurrentTime);
|
||||
|
||||
// difference between localCurrentTime and serverTime is 60 seconds plus the round trip time of 1 second
|
||||
const allowedClockSkewInSec = 61;
|
||||
service.isLocalTimeOutOfSync(allowedClockSkewInSec).subscribe(isOutOfSync => {
|
||||
service.isLocalTimeOutOfSync(allowedClockSkewInSec).subscribe((isOutOfSync) => {
|
||||
expect(isOutOfSync).toBeFalse();
|
||||
});
|
||||
|
||||
@@ -203,6 +181,4 @@ describe('TimeSyncService', () => {
|
||||
req.flush(serverTime);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
+66
-87
@@ -1,50 +1,29 @@
|
||||
<mat-label
|
||||
class="adf-property-label"
|
||||
[attr.data-automation-id]="'card-dateitem-label-' + property.key"
|
||||
*ngIf="showProperty && !isEditable"
|
||||
[attr.for]="'card-view-dateitem-' + property.key"
|
||||
[ngClass]="{ 'adf-property-readonly-value': isReadonlyProperty, 'adf-property-value-editable': editable }"
|
||||
[title]="'CORE.METADATA.ACTIONS.COPY_TO_CLIPBOARD' | translate"
|
||||
>
|
||||
{{ property.label | translate }}
|
||||
</mat-label>
|
||||
|
||||
<mat-form-field
|
||||
*ngIf="showProperty && !isEditable && !property.multivalued"
|
||||
[attr.data-automation-id]="'card-' + property.type + '-value-' + property.key"
|
||||
(dblclick)="copyToClipboard(property.displayValue)"
|
||||
[ngClass]="{ 'adf-property-read-only': !isEditable }"
|
||||
class="adf-property-field">
|
||||
<mat-label
|
||||
class="adf-property-label"
|
||||
[ngClass]="{
|
||||
'adf-property-value-editable': editable,
|
||||
'adf-property-readonly-value': isReadonlyProperty
|
||||
}"
|
||||
[attr.data-automation-id]="'card-dateitem-label-' + property.key"
|
||||
[attr.for]="'card-view-dateitem-' + property.key"
|
||||
[title]="'CORE.METADATA.ACTIONS.COPY_TO_CLIPBOARD' | translate"
|
||||
>
|
||||
{{ property.label | translate }}
|
||||
</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[attr.data-automation-id]="'card-dateitem-' + property.key"
|
||||
[value]="property.displayValue"
|
||||
title="{{ property.label | translate }}"
|
||||
[attr.id]="'card-view-dateitem-' + property.key"
|
||||
[attr.tabIndex]="-1"
|
||||
[attr.aria-label]="property.label | translate"
|
||||
class="adf-property-value"
|
||||
[ngClass]="{
|
||||
'adf-property-value-editable': editable,
|
||||
'adf-property-readonly-value': isReadonlyProperty,
|
||||
}"
|
||||
readonly
|
||||
disabled
|
||||
/>
|
||||
</mat-form-field>
|
||||
|
||||
<div
|
||||
class="adf-property-value"
|
||||
[ngClass]="{ 'adf-property-value-editable': editable, 'adf-property-readonly-value': isReadonlyProperty }"
|
||||
>
|
||||
|
||||
<mat-form-field *ngIf="isEditable && !property.multivalued" class="adf-dateitem-editable adf-property-field hxp-input" [floatLabel]="property.default ? 'always' : null">
|
||||
<div class="adf-property-value" [ngClass]="{ 'adf-property-value-editable': editable, 'adf-property-readonly-value': isReadonlyProperty }">
|
||||
<span *ngIf="!isEditable && !property.multivalued" [attr.data-automation-id]="'card-' + property.type + '-value-' + property.key">
|
||||
<span
|
||||
*ngIf="showProperty"
|
||||
[attr.data-automation-id]="'card-dateitem-' + property.key"
|
||||
(dblclick)="copyToClipboard(property.displayValue)"
|
||||
[title]="'CORE.METADATA.ACTIONS.COPY_TO_CLIPBOARD' | translate"
|
||||
>{{ property.displayValue }}</span
|
||||
>
|
||||
</span>
|
||||
<mat-form-field *ngIf="isEditable && !property.multivalued" class="adf-dateitem-editable hxp-input" [floatLabel]="property.default ? 'always' : null">
|
||||
<mat-label
|
||||
*ngIf="isEditable"
|
||||
class="adf-property-label"
|
||||
[attr.data-automation-id]="'card-dateitem-label-' + property.key"
|
||||
*ngIf="isEditable"
|
||||
[attr.for]="'card-view-dateitem-' + property.key"
|
||||
[ngClass]="{ 'adf-property-readonly-value': isReadonlyProperty, 'adf-property-value-editable': editable }"
|
||||
[title]="'CORE.METADATA.ACTIONS.COPY_TO_CLIPBOARD' | translate"
|
||||
@@ -100,51 +79,51 @@
|
||||
[startAt]="valueDate"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<ng-template #elseEmptyValueBlock>
|
||||
{{ property.default | translate }}
|
||||
</ng-template>
|
||||
<ng-template #elseEmptyValueBlock>
|
||||
{{ property.default | translate }}
|
||||
</ng-template>
|
||||
|
||||
<div *ngIf="property.multivalued" class="adf-property-field adf-dateitem-chip-list-container adf-dateitem-editable">
|
||||
<mat-chip-listbox #chipList class="adf-textitem-chip-list">
|
||||
<mat-chip-option
|
||||
*ngFor="let propertyValue of property.displayValue; let idx = index"
|
||||
[removable]="isEditable"
|
||||
(removed)="removeValueFromList(idx)"
|
||||
<div *ngIf="property.multivalued" class="adf-property-field adf-dateitem-chip-list-container adf-dateitem-editable">
|
||||
<mat-chip-listbox #chipList class="adf-textitem-chip-list">
|
||||
<mat-chip-option
|
||||
*ngFor="let propertyValue of property.displayValue; let idx = index"
|
||||
[removable]="isEditable"
|
||||
(removed)="removeValueFromList(idx)"
|
||||
>
|
||||
{{ propertyValue }}
|
||||
<mat-icon *ngIf="isEditable" matChipRemove>cancel</mat-icon>
|
||||
</mat-chip-option>
|
||||
</mat-chip-listbox>
|
||||
|
||||
<div
|
||||
*ngIf="isEditable"
|
||||
class="adf-property-field adf-dateitem-editable-controls"
|
||||
(click)="showDatePicker()"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
(keyup.enter)="showDatePicker()"
|
||||
>
|
||||
{{ propertyValue }}
|
||||
<mat-icon *ngIf="isEditable" matChipRemove>cancel</mat-icon>
|
||||
</mat-chip-option>
|
||||
</mat-chip-listbox>
|
||||
|
||||
<div
|
||||
*ngIf="isEditable"
|
||||
class="adf-property-field adf-dateitem-editable-controls"
|
||||
(click)="showDatePicker()"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
(keyup.enter)="showDatePicker()"
|
||||
>
|
||||
<input
|
||||
class="adf-invisible-date-input"
|
||||
[attr.tabIndex]="-1"
|
||||
[matDatetimepicker]="datetimePicker"
|
||||
(dateChange)="addDateToList($event)"
|
||||
[attr.id]="'card-view-dateitem-' + property.key"
|
||||
/>
|
||||
<mat-datetimepicker-toggle
|
||||
[attr.tabindex]="-1"
|
||||
matSuffix
|
||||
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
|
||||
[attr.data-automation-id]="'datepickertoggle-' + property.key"
|
||||
[for]="datetimePicker"
|
||||
/>
|
||||
<mat-datetimepicker
|
||||
#datetimePicker
|
||||
[type]="$any(property).type"
|
||||
[timeInterval]="5"
|
||||
[attr.data-automation-id]="'datepicker-' + property.key"
|
||||
[startAt]="valueDate"
|
||||
/>
|
||||
<input
|
||||
class="adf-invisible-date-input"
|
||||
[attr.tabIndex]="-1"
|
||||
[matDatetimepicker]="datetimePicker"
|
||||
(dateChange)="addDateToList($event)"
|
||||
[attr.id]="'card-view-dateitem-' + property.key"
|
||||
/>
|
||||
<mat-datetimepicker-toggle
|
||||
[attr.tabindex]="-1"
|
||||
matSuffix
|
||||
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
|
||||
[attr.data-automation-id]="'datepickertoggle-' + property.key"
|
||||
[for]="datetimePicker"
|
||||
/>
|
||||
<mat-datetimepicker
|
||||
#datetimePicker
|
||||
[type]="$any(property).type"
|
||||
[timeInterval]="5"
|
||||
[attr.data-automation-id]="'datepicker-' + property.key"
|
||||
[startAt]="valueDate"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+4
-5
@@ -64,14 +64,14 @@ describe('CardViewDateItemComponent', () => {
|
||||
afterEach(() => fixture.destroy());
|
||||
|
||||
const getPropertyLabel = (): string => testingUtils.getInnerTextByCSS('.adf-property-label');
|
||||
const getPropertyInputValue = (): HTMLInputElement => testingUtils.getInputByCSS('.adf-property-value');
|
||||
const getPropertyValue = (): string => testingUtils.getInnerTextByCSS('.adf-property-value');
|
||||
const getDateTime = (): string => testingUtils.getInnerTextByCSS('.adf-datepicker-span-button');
|
||||
|
||||
it('should render the label and value', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getPropertyLabel()).toBe('Date label');
|
||||
expect(getPropertyInputValue().value.trim()).toBe('Jul 10, 2017');
|
||||
expect(getPropertyValue().trim()).toBe('Jul 10, 2017');
|
||||
});
|
||||
|
||||
it('should NOT render the default as value if the value is empty, editable:false and displayEmpty is false', () => {
|
||||
@@ -87,7 +87,6 @@ describe('CardViewDateItemComponent', () => {
|
||||
component.displayEmpty = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
const getPropertyValue = (): string => testingUtils.getInnerTextByCSS('.adf-property-value');
|
||||
expect(getPropertyValue().trim()).toBe('');
|
||||
});
|
||||
|
||||
@@ -104,7 +103,7 @@ describe('CardViewDateItemComponent', () => {
|
||||
component.displayEmpty = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getPropertyInputValue().value.trim()).toBe('FAKE-DEFAULT-KEY');
|
||||
expect(getPropertyValue().trim()).toBe('FAKE-DEFAULT-KEY');
|
||||
});
|
||||
|
||||
it('should render the default as value if the value is empty and editable:true', () => {
|
||||
@@ -214,7 +213,7 @@ describe('CardViewDateItemComponent', () => {
|
||||
component.editable = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
testingUtils.doubleClickByDataAutomationId(`card-${component.property.type}-value-${component.property.key}`);
|
||||
testingUtils.doubleClickByDataAutomationId(`card-dateitem-${component.property.key}`);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(clipboardService.copyContentToClipboard).toHaveBeenCalledWith('Jul 10, 2017', 'CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
|
||||
|
||||
+40
-43
@@ -1,51 +1,48 @@
|
||||
<ng-container *ngIf="!property.isEmpty() || isEditable">
|
||||
<div [ngSwitch]="templateType">
|
||||
<div *ngSwitchDefault>
|
||||
<mat-form-field
|
||||
*ngIf="!isEditable"
|
||||
[attr.data-automation-id]="'select-readonly-value-' + property.key"
|
||||
[title]="property.displayValue | async | translate"
|
||||
class="adf-property-list adf-property adf-property-field"
|
||||
>
|
||||
<mat-label
|
||||
[attr.data-automation-id]="'card-select-label-' + property.key"
|
||||
>
|
||||
{{ property.label | translate }}
|
||||
</mat-label>
|
||||
<input
|
||||
matInput
|
||||
class="adf-property-value"
|
||||
[attr.tabIndex]="-1"
|
||||
[value]="property.displayValue | async | translate"
|
||||
<div *ngIf="!isEditable"
|
||||
[attr.data-automation-id]="'card-select-label-' + property.key"
|
||||
class="adf-property-label adf-property-label-non-editable"
|
||||
>{{ property.label | translate }}
|
||||
</div>
|
||||
<div class="adf-property-field">
|
||||
<div
|
||||
*ngIf="!isEditable"
|
||||
class="adf-property-value adf-property-read-only"
|
||||
[attr.data-automation-id]="'select-readonly-value-' + property.key"
|
||||
data-automation-class="read-only-value"
|
||||
disabled
|
||||
readonly
|
||||
/>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="adf-property-value" *ngIf="isEditable">
|
||||
<mat-label
|
||||
[attr.data-automation-id]="'card-select-label-' + property.key"
|
||||
class="adf-property-label adf-property-value-editable"
|
||||
[ngClass]="{
|
||||
'adf-property-readonly-value': isReadonlyProperty
|
||||
}"
|
||||
>{{ property.label | translate }}
|
||||
</mat-label>
|
||||
<mat-select
|
||||
[(value)]="value"
|
||||
[ngClass]="{ 'adf-property-readonly-value': isReadonlyProperty }"
|
||||
panelClass="adf-select-filter"
|
||||
(selectionChange)="onChange($event)"
|
||||
data-automation-class="select-box"
|
||||
[aria-label]="property.label | translate"
|
||||
[title]="property.displayValue | async | translate"
|
||||
>
|
||||
<adf-select-filter-input *ngIf="showInputFilter" (change)="onFilterInputChange($event)" />
|
||||
<mat-option *ngIf="displayNoneOption">{{ 'CORE.CARDVIEW.NONE' | translate }}</mat-option>
|
||||
<mat-option *ngFor="let option of list$ | async" [value]="option.key">
|
||||
{{ option.label | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
{{ property.displayValue | async | translate }}
|
||||
</div>
|
||||
<div *ngIf="isEditable">
|
||||
<mat-form-field class="adf-property-value">
|
||||
<mat-label
|
||||
[attr.data-automation-id]="'card-select-label-' + property.key"
|
||||
class="adf-property-label adf-property-value-editable"
|
||||
[ngClass]="{
|
||||
'adf-property-readonly-value': isReadonlyProperty
|
||||
}"
|
||||
>{{ property.label | translate }}
|
||||
</mat-label>
|
||||
<mat-select
|
||||
[(value)]="value"
|
||||
[ngClass]="{ 'adf-property-readonly-value': isReadonlyProperty }"
|
||||
panelClass="adf-select-filter"
|
||||
(selectionChange)="onChange($event)"
|
||||
data-automation-class="select-box"
|
||||
[aria-label]="property.label | translate"
|
||||
>
|
||||
<adf-select-filter-input *ngIf="showInputFilter" (change)="onFilterInputChange($event)" />
|
||||
<mat-option *ngIf="displayNoneOption">{{ 'CORE.CARDVIEW.NONE' | translate }}</mat-option>
|
||||
<mat-option *ngFor="let option of list$ | async" [value]="option.key">
|
||||
{{ option.label | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngSwitchCase="'autocompleteBased'">
|
||||
<mat-form-field
|
||||
|
||||
+4
-9
@@ -25,7 +25,7 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
|
||||
import { UnitTestingUtils } from '../../../testing/unit-testing-utils';
|
||||
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
||||
import { DebugElement, SimpleChange } from '@angular/core';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
describe('CardViewSelectItemComponent', () => {
|
||||
let loader: HarnessLoader;
|
||||
@@ -85,8 +85,7 @@ describe('CardViewSelectItemComponent', () => {
|
||||
editable: false
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(testingUtils.getInnerTextByDataAutomationId('card-select-label-key')).toBe('Select box label');
|
||||
expect(testingUtils.getInnerTextByCSS('.adf-property-label')).toBe('Select box label');
|
||||
});
|
||||
|
||||
it('should render readOnly value is editable property is FALSE', () => {
|
||||
@@ -110,9 +109,7 @@ describe('CardViewSelectItemComponent', () => {
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const inputEl = getReadOnlyElement().nativeElement as HTMLInputElement;
|
||||
expect(inputEl.value).toBe('Two');
|
||||
expect(getReadOnlyElement().nativeElement.title).toBe('Two');
|
||||
});
|
||||
|
||||
it('should be possible edit selectBox item', async () => {
|
||||
@@ -287,9 +284,7 @@ describe('CardViewSelectItemComponent', () => {
|
||||
const autocompleteValueSpy = spyOn(cardViewUpdateService.autocompleteInputValue$, 'next');
|
||||
component.editedValue = '';
|
||||
component.editable = true;
|
||||
component.ngOnChanges({
|
||||
property: new SimpleChange(undefined, component.property, true)
|
||||
});
|
||||
component.ngOnChanges({ property: { firstChange: true } } as any);
|
||||
fixture.detectChanges();
|
||||
|
||||
component.autocompleteControl.setValue('new value');
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('CardViewComponent', () => {
|
||||
|
||||
const getPropertyLabel = (): string => testingUtils.getInnerTextByCSS('.adf-property-label');
|
||||
const getPropertyValue = (): string => testingUtils.getByCSS('.adf-property-value').nativeElement.value;
|
||||
const getPropertyValueText = (): string => testingUtils.getInputByCSS('.adf-property-value').value;
|
||||
const getPropertyValueText = (): string => testingUtils.getInnerTextByCSS('.adf-property-value');
|
||||
const getPropertyValueByDataAutomationId = (dataAutomationId: string): string =>
|
||||
testingUtils.getByDataAutomationId(dataAutomationId).nativeElement.value;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import { UnitTestingUtils } from '../../../testing/unit-testing-utils';
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-custom-column-template-component',
|
||||
@@ -1353,7 +1354,7 @@ describe('DataTable', () => {
|
||||
expect(rows[1].isSelected).toBeFalsy();
|
||||
|
||||
dataTable.resetSelection();
|
||||
const rowClickPromise = dataTable.rowClick.pipe(take(1)).toPromise();
|
||||
const rowClickPromise = firstValueFrom(dataTable.rowClick.pipe(take(1)));
|
||||
testingUtils.clickByCSS('[data-automation-id="datatable-row-0"] > div');
|
||||
fixture.detectChanges();
|
||||
await rowClickPromise;
|
||||
@@ -1363,7 +1364,7 @@ describe('DataTable', () => {
|
||||
expect(rows2[1].isSelected).toBeFalsy();
|
||||
|
||||
dataTable.resetSelection();
|
||||
const cellClickPromise = dataTable.rowClick.pipe(take(1)).toPromise();
|
||||
const cellClickPromise = firstValueFrom(dataTable.rowClick.pipe(take(1)));
|
||||
testingUtils.clickByCSS('[data-automation-id="datatable-row-1"] > div');
|
||||
fixture.detectChanges();
|
||||
await cellClickPromise;
|
||||
|
||||
@@ -1,37 +1,42 @@
|
||||
<h1 mat-dialog-title data-automation-id="adf-confirm-dialog-title">{{ title | translate }}</h1>
|
||||
<mat-dialog-content>
|
||||
<div class="adf-confirm-dialog-content">
|
||||
<p *ngIf="!htmlContent; else customContent" data-automation-id="adf-confirm-dialog-base-message">
|
||||
{{ message | translate }}
|
||||
</p>
|
||||
<p *ngIf="!htmlContent; else customContent" data-automation-id="adf-confirm-dialog-base-message">{{ message | translate }}</p>
|
||||
<ng-template #customContent>
|
||||
<span [innerHTML]="sanitizedHtmlContent()" data-automation-id="adf-confirm-dialog-custom-content"></span>
|
||||
<span [innerHTML]="sanitizedHtmlContent()" data-automation-id="adf-confirm-dialog-custom-content"></span>
|
||||
</ng-template>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<span class="adf-dialog-spacer" data-automation-id="adf-confirm-dialog-spacer"></span>
|
||||
<button id="adf-confirm-accept"
|
||||
class="adf-confirm-dialog-button"
|
||||
mat-button
|
||||
color="primary"
|
||||
data-automation-id="adf-confirm-dialog-confirmation"
|
||||
[mat-dialog-close]="true">
|
||||
{{ yesLabel | translate }}
|
||||
</button>
|
||||
<button *ngIf="thirdOptionLabel"
|
||||
id="adf-confirm-all"
|
||||
class="adf-confirm-dialog-button"
|
||||
mat-button
|
||||
[mat-dialog-close]="thirdOptionLabel"
|
||||
data-automation-id="adf-confirm-dialog-confirm-all">{{ thirdOptionLabel | translate }}
|
||||
</button>
|
||||
<button id="adf-confirm-cancel"
|
||||
class="adf-confirm-dialog-button"
|
||||
mat-button
|
||||
[mat-dialog-close]="false"
|
||||
data-automation-id="adf-confirm-dialog-reject"
|
||||
cdkFocusInitial>
|
||||
<button
|
||||
id="adf-confirm-cancel"
|
||||
class="adf-confirm-dialog-button"
|
||||
mat-button
|
||||
[mat-dialog-close]="false"
|
||||
data-automation-id="adf-confirm-dialog-reject"
|
||||
>
|
||||
{{ noLabel | translate }}
|
||||
</button>
|
||||
<button
|
||||
*ngIf="thirdOptionLabel"
|
||||
id="adf-confirm-all"
|
||||
class="adf-confirm-dialog-button"
|
||||
mat-button
|
||||
[mat-dialog-close]="thirdOptionLabel"
|
||||
data-automation-id="adf-confirm-dialog-confirm-all"
|
||||
>
|
||||
{{ thirdOptionLabel | translate }}
|
||||
</button>
|
||||
<button
|
||||
id="adf-confirm-accept"
|
||||
class="adf-confirm-dialog-button"
|
||||
mat-button
|
||||
color="primary"
|
||||
data-automation-id="adf-confirm-dialog-confirmation"
|
||||
[mat-dialog-close]="true"
|
||||
cdkFocusInitial
|
||||
>
|
||||
{{ yesLabel | translate }}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
|
||||
@@ -366,6 +366,44 @@ describe('FormFieldValidator', () => {
|
||||
expect(validator.validate(field)).toBe(false);
|
||||
expect(field.validationSummary).not.toBeNull();
|
||||
});
|
||||
|
||||
describe('MaxLengthFieldValidator with custom value', () => {
|
||||
let customValidator: MaxLengthFieldValidator;
|
||||
|
||||
beforeEach(() => {
|
||||
customValidator = new MaxLengthFieldValidator([FormFieldTypes.NUMBER], 3);
|
||||
});
|
||||
|
||||
it('should validate integer values', () => {
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER,
|
||||
value: '444'
|
||||
});
|
||||
|
||||
const isValid = customValidator.validate(field);
|
||||
expect(isValid).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate values exceeding maxLength', () => {
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER,
|
||||
value: '4444'
|
||||
});
|
||||
|
||||
const isValid = customValidator.validate(field);
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
|
||||
it('should not validate not supported fields', () => {
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
value: 'abcd'
|
||||
});
|
||||
|
||||
const isSupported = customValidator.isSupported(field);
|
||||
expect(isSupported).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MinValueFieldValidator', () => {
|
||||
|
||||
@@ -129,23 +129,33 @@ export class MinLengthFieldValidator implements FormFieldValidator {
|
||||
}
|
||||
|
||||
export class MaxLengthFieldValidator implements FormFieldValidator {
|
||||
private supportedTypes = [FormFieldTypes.TEXT, FormFieldTypes.MULTILINE_TEXT];
|
||||
constructor(
|
||||
private supportedTypes: FormFieldTypes[] = [FormFieldTypes.TEXT, FormFieldTypes.MULTILINE_TEXT],
|
||||
private maxLength?: number
|
||||
) {}
|
||||
|
||||
isSupported(field: FormFieldModel): boolean {
|
||||
return field && this.supportedTypes.indexOf(field.type) > -1 && field.maxLength > 0;
|
||||
return field && this.supportedTypes.indexOf(field.type) > -1 && this.getMaxLength(field) > 0;
|
||||
}
|
||||
|
||||
validate(field: FormFieldModel): boolean {
|
||||
if (this.isSupported(field) && field.value && field.isVisible) {
|
||||
if (field.value.length <= field.maxLength) {
|
||||
if (field.value.toString().length <= this.getMaxLength(field)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
field.validationSummary.message = `FORM.FIELD.VALIDATOR.NO_LONGER_THAN`;
|
||||
field.validationSummary.attributes.set('maxLength', field.maxLength.toLocaleString());
|
||||
field.validationSummary.attributes.set('maxLength', this.getMaxLength(field).toLocaleString());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getMaxLength(field: FormFieldModel): number | undefined {
|
||||
return this.maxLength ?? field.maxLength;
|
||||
}
|
||||
}
|
||||
|
||||
export class MinValueFieldValidator implements FormFieldValidator {
|
||||
@@ -299,6 +309,7 @@ export const FORM_FIELD_VALIDATORS = [
|
||||
new NumberFieldValidator(),
|
||||
new MinLengthFieldValidator(),
|
||||
new MaxLengthFieldValidator(),
|
||||
new MaxLengthFieldValidator([FormFieldTypes.NUMBER], 10),
|
||||
new MinValueFieldValidator(),
|
||||
new MaxValueFieldValidator(),
|
||||
new RegExFieldValidator(),
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
overflow-wrap: anywhere;
|
||||
gap: 8px;
|
||||
|
||||
mat-icon {
|
||||
margin-right: 8px;
|
||||
flex: 0 0 auto;
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@alfresco/eslint-plugin-eslint-angular",
|
||||
"version": "8.1.0",
|
||||
"version": "8.3.0",
|
||||
"description": "Alfresco ADF eslint angular custom rules",
|
||||
"main": "main.js",
|
||||
"author": "Hyland Software, Inc. and its affiliates",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@alfresco/adf-extensions",
|
||||
"description": "Provides extensibility support for ADF applications.",
|
||||
"version": "8.1.0",
|
||||
"version": "8.3.0",
|
||||
"license": "Apache-2.0",
|
||||
"author": "Hyland Software, Inc. and its affiliates",
|
||||
"repository": {
|
||||
@@ -14,7 +14,7 @@
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=14.1.3",
|
||||
"@angular/core": ">=14.1.3",
|
||||
"@alfresco/js-api": ">=9.1.0"
|
||||
"@alfresco/js-api": ">=9.1.1"
|
||||
},
|
||||
"keywords": [
|
||||
"extensions",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@alfresco/adf-insights",
|
||||
"description": "Alfresco ADF insights",
|
||||
"version": "8.1.0",
|
||||
"version": "8.3.0",
|
||||
"author": "Hyland Software, Inc. and its affiliates",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -11,8 +11,8 @@
|
||||
"url": "https://github.com/Alfresco/alfresco-ng2-components/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alfresco/adf-core": ">=8.1.0",
|
||||
"@alfresco/adf-content-services": ">=8.1.0",
|
||||
"@alfresco/adf-core": ">=8.2.0",
|
||||
"@alfresco/adf-content-services": ">=8.2.0",
|
||||
"@ngx-translate/core": ">=14.0.0",
|
||||
"chart.js": "^4.3.0",
|
||||
"ng2-charts": "^4.1.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@alfresco/js-api",
|
||||
"version": "9.1.0",
|
||||
"version": "9.3.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "JavaScript client library for the Alfresco REST API",
|
||||
"author": "Hyland Software, Inc. and its affiliates",
|
||||
@@ -15,7 +15,7 @@
|
||||
"url": "https://github.com/Alfresco/alfresco-ng2-components/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"event-emitter": "^0.3.5",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"superagent": "^9.0.1",
|
||||
"tslib": "^2.6.1"
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import ee, { EmitterMethod, Emitter } from 'event-emitter';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { ContentAuth } from './authentication/contentAuth';
|
||||
import { ProcessAuth } from './authentication/processAuth';
|
||||
import { Oauth2Auth } from './authentication/oauth2Auth';
|
||||
@@ -26,11 +26,12 @@ import { AlfrescoApiConfig } from './alfrescoApiConfig';
|
||||
import { Authentication } from './authentication/authentication';
|
||||
import { AlfrescoApiType } from './to-deprecate/alfresco-api-type';
|
||||
import { HttpClient } from './api-clients/http-client.interface';
|
||||
import { AlfrescoApiClient, AlfrescoApiClientPromise } from './alfrescoApiClient';
|
||||
|
||||
export class AlfrescoApi implements Emitter, AlfrescoApiType {
|
||||
type EventEmitterInstance = InstanceType<typeof EventEmitter>;
|
||||
|
||||
export class AlfrescoApi extends AlfrescoApiClient implements AlfrescoApiType {
|
||||
__type = 'legacy-client';
|
||||
storage: Storage;
|
||||
config: AlfrescoApiConfig;
|
||||
contentClient: ContentClient;
|
||||
contentPrivateClient: ContentClient;
|
||||
processClient: ProcessClient;
|
||||
@@ -43,18 +44,11 @@ export class AlfrescoApi implements Emitter, AlfrescoApiType {
|
||||
processAuth: ProcessAuth;
|
||||
contentAuth: ContentAuth;
|
||||
|
||||
on: EmitterMethod;
|
||||
off: EmitterMethod;
|
||||
once: EmitterMethod;
|
||||
|
||||
bufferEvents: string[] = [];
|
||||
|
||||
emit: (type: string, ...args: any[]) => void;
|
||||
|
||||
username: string;
|
||||
|
||||
constructor(config?: AlfrescoApiConfig, public httpClient?: HttpClient) {
|
||||
ee(this);
|
||||
constructor(config?: AlfrescoApiConfig, httpClient?: HttpClient) {
|
||||
super(undefined, httpClient);
|
||||
|
||||
if (config) {
|
||||
this.setConfig(config);
|
||||
@@ -392,14 +386,15 @@ export class AlfrescoApi implements Emitter, AlfrescoApiType {
|
||||
return this.contentAuth.validateTicket();
|
||||
}
|
||||
|
||||
private loginBPMECM(username: string, password: string): Promise<[string, string]> {
|
||||
private loginBPMECM(username: string, password: string): AlfrescoApiClientPromise<[string, string]> {
|
||||
const contentPromise = this.contentAuth.login(username, password);
|
||||
const processPromise = this.processAuth.login(username, password);
|
||||
|
||||
const promise: any = new Promise<[string, string]>((resolve, reject) => {
|
||||
const eventEmitter: EventEmitterInstance = new EventEmitter();
|
||||
const promise = new Promise<[string, string]>((resolve, reject) => {
|
||||
Promise.all([contentPromise, processPromise]).then(
|
||||
(data) => {
|
||||
promise.emit('success');
|
||||
eventEmitter.emit('success');
|
||||
resolve(data);
|
||||
},
|
||||
(error) => {
|
||||
@@ -407,16 +402,15 @@ export class AlfrescoApi implements Emitter, AlfrescoApiType {
|
||||
this.processAuth.invalidateSession();
|
||||
|
||||
if (error.status === 401) {
|
||||
promise.emit('unauthorized');
|
||||
eventEmitter.emit('unauthorized');
|
||||
}
|
||||
promise.emit('error');
|
||||
eventEmitter.emit('error');
|
||||
reject(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
ee(promise); // jshint ignore:line
|
||||
return promise;
|
||||
return this.addPromiseListeners(promise, eventEmitter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -449,29 +443,29 @@ export class AlfrescoApi implements Emitter, AlfrescoApiType {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private _logoutBPMECM(): Promise<void> {
|
||||
private _logoutBPMECM(): AlfrescoApiClientPromise<void> {
|
||||
const contentPromise = this.contentAuth.logout();
|
||||
const processPromise = this.processAuth.logout();
|
||||
|
||||
const promise: any = new Promise<void>((resolve, reject) => {
|
||||
const eventEmitter: EventEmitterInstance = new EventEmitter();
|
||||
const promise = new Promise<void>((resolve, reject) => {
|
||||
Promise.all([contentPromise, processPromise]).then(
|
||||
() => {
|
||||
this.config.ticket = undefined;
|
||||
promise.emit('logout');
|
||||
eventEmitter.emit('logout');
|
||||
resolve();
|
||||
},
|
||||
(error) => {
|
||||
if (error.status === 401) {
|
||||
promise.emit('unauthorized');
|
||||
eventEmitter.emit('unauthorized');
|
||||
}
|
||||
promise.emit('error');
|
||||
eventEmitter.emit('error');
|
||||
reject(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
ee(promise); // jshint ignore:line
|
||||
return promise;
|
||||
return this.addPromiseListeners(promise, eventEmitter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import ee from 'event-emitter';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { AlfrescoApiConfig } from './alfrescoApiConfig';
|
||||
import { Authentication } from './authentication/authentication';
|
||||
import { SuperagentHttpClient } from './superagentHttpClient';
|
||||
@@ -25,11 +25,13 @@ import { Storage } from './storage';
|
||||
|
||||
declare const Buffer: any;
|
||||
|
||||
type EventEmitterInstance = InstanceType<typeof EventEmitter>;
|
||||
|
||||
export type AlfrescoApiClientPromise<T = any> = Promise<T> & {
|
||||
on: ee.EmitterMethod;
|
||||
off: ee.EmitterMethod;
|
||||
once: ee.EmitterMethod;
|
||||
emit: (type: string, ...args: any[]) => void;
|
||||
on: <K extends string | symbol>(event: K, fn: (...args: any[]) => void, context?: any) => AlfrescoApiClientPromise<T>;
|
||||
off: <K extends string | symbol>(event: K, fn?: (...args: any[]) => void, context?: any) => AlfrescoApiClientPromise<T>;
|
||||
once: <K extends string | symbol>(event: K, fn: (...args: any[]) => void, context?: any) => AlfrescoApiClientPromise<T>;
|
||||
emit: <K extends string | symbol>(event: K, ...args: any[]) => boolean;
|
||||
abort?: () => void;
|
||||
};
|
||||
|
||||
@@ -62,11 +64,8 @@ export function buildCollectionParam(param: string[], collectionFormat: string):
|
||||
}
|
||||
}
|
||||
|
||||
export class AlfrescoApiClient implements ee.Emitter, LegacyHttpClient {
|
||||
on: ee.EmitterMethod;
|
||||
off: ee.EmitterMethod;
|
||||
once: ee.EmitterMethod;
|
||||
emit: (type: string, ...args: any[]) => void;
|
||||
export class AlfrescoApiClient implements LegacyHttpClient {
|
||||
private eventEmitter = new EventEmitter();
|
||||
|
||||
storage: Storage;
|
||||
host: string;
|
||||
@@ -105,13 +104,29 @@ export class AlfrescoApiClient implements ee.Emitter, LegacyHttpClient {
|
||||
|
||||
constructor(host?: string, httpClient?: HttpClient) {
|
||||
this.host = host;
|
||||
|
||||
this.storage = Storage.getInstance();
|
||||
|
||||
// fallback for backward compatibility
|
||||
this.httpClient = httpClient || new SuperagentHttpClient();
|
||||
}
|
||||
|
||||
ee(this);
|
||||
// EventEmitter delegation methods
|
||||
on<K extends string | symbol>(event: K, fn: (...args: any[]) => void, context?: any): this {
|
||||
this.eventEmitter.on(event, fn, context);
|
||||
return this;
|
||||
}
|
||||
|
||||
off<K extends string | symbol>(event: K, fn?: (...args: any[]) => void, context?: any): this {
|
||||
this.eventEmitter.off(event, fn, context);
|
||||
return this;
|
||||
}
|
||||
|
||||
once<K extends string | symbol>(event: K, fn: (...args: any[]) => void, context?: any): this {
|
||||
this.eventEmitter.once(event, fn, context);
|
||||
return this;
|
||||
}
|
||||
|
||||
emit<K extends string | symbol>(event: K, ...args: any[]): boolean {
|
||||
return this.eventEmitter.emit(event, ...args);
|
||||
}
|
||||
|
||||
request<T = any>(options: RequestOptions): Promise<T> {
|
||||
@@ -325,7 +340,7 @@ export class AlfrescoApiClient implements ee.Emitter, LegacyHttpClient {
|
||||
|
||||
return {
|
||||
apiClientEmitter,
|
||||
eventEmitter: ee({})
|
||||
eventEmitter: new EventEmitter()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -374,27 +389,22 @@ export class AlfrescoApiClient implements ee.Emitter, LegacyHttpClient {
|
||||
return Boolean(contentType?.match(/^application\/json(;.*)?$/i));
|
||||
}
|
||||
|
||||
private addPromiseListeners<T = any>(promise: Promise<T>, eventEmitter: ee.Emitter): AlfrescoApiClientPromise<T> {
|
||||
addPromiseListeners<T = any>(promise: Promise<T>, eventEmitter: EventEmitterInstance): AlfrescoApiClientPromise<T> {
|
||||
return Object.assign(promise, {
|
||||
on() {
|
||||
// eslint-disable-next-line prefer-spread,prefer-rest-params
|
||||
eventEmitter.on.apply(eventEmitter, arguments);
|
||||
return this;
|
||||
on<K extends string | symbol>(event: K, fn: (...args: any[]) => void, context?: any): AlfrescoApiClientPromise<T> {
|
||||
eventEmitter.on(event, fn, context);
|
||||
return this as AlfrescoApiClientPromise<T>;
|
||||
},
|
||||
once() {
|
||||
// eslint-disable-next-line prefer-spread,prefer-rest-params
|
||||
eventEmitter.once.apply(eventEmitter, arguments);
|
||||
return this;
|
||||
once<K extends string | symbol>(event: K, fn: (...args: any[]) => void, context?: any): AlfrescoApiClientPromise<T> {
|
||||
eventEmitter.once(event, fn, context);
|
||||
return this as AlfrescoApiClientPromise<T>;
|
||||
},
|
||||
emit() {
|
||||
// eslint-disable-next-line prefer-spread,prefer-rest-params
|
||||
eventEmitter.emit.apply(eventEmitter, arguments);
|
||||
return this;
|
||||
emit<K extends string | symbol>(event: K, ...args: any[]): boolean {
|
||||
return eventEmitter.emit(event, ...args);
|
||||
},
|
||||
off() {
|
||||
// eslint-disable-next-line prefer-spread,prefer-rest-params
|
||||
eventEmitter.off.apply(eventEmitter, arguments);
|
||||
return this;
|
||||
off<K extends string | symbol>(event: K, fn?: (...args: any[]) => void, context?: any): AlfrescoApiClientPromise<T> {
|
||||
eventEmitter.off(event, fn, context);
|
||||
return this as AlfrescoApiClientPromise<T>;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
*/
|
||||
|
||||
import { Authentication } from '../authentication/authentication';
|
||||
import { Emitter } from 'event-emitter';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
|
||||
type EventEmitterInstance = InstanceType<typeof EventEmitter>;
|
||||
|
||||
export interface RequestOptions {
|
||||
path: string;
|
||||
@@ -93,8 +95,13 @@ export interface SecurityOptions {
|
||||
}
|
||||
|
||||
export interface Emitters {
|
||||
readonly eventEmitter: Emitter;
|
||||
readonly apiClientEmitter: Emitter;
|
||||
readonly eventEmitter: EventEmitterInstance;
|
||||
readonly apiClientEmitter: {
|
||||
on: EventEmitterInstance['on'];
|
||||
off: EventEmitterInstance['off'];
|
||||
once: EventEmitterInstance['once'];
|
||||
emit: EventEmitterInstance['emit'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface HttpClient {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import ee from 'event-emitter';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { AuthenticationApi, TicketBody } from '../api/auth-rest-api';
|
||||
import { AlfrescoApiClient } from '../alfrescoApiClient';
|
||||
import { AlfrescoApiConfig } from '../alfrescoApiConfig';
|
||||
@@ -104,8 +104,7 @@ export class ContentAuth extends AlfrescoApiClient {
|
||||
});
|
||||
});
|
||||
|
||||
ee(promise); // jshint ignore:line
|
||||
return promise;
|
||||
return this.addPromiseListeners(promise, new EventEmitter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,8 +132,7 @@ export class ContentAuth extends AlfrescoApiClient {
|
||||
);
|
||||
});
|
||||
|
||||
ee(promise); // jshint ignore:line
|
||||
return promise;
|
||||
return this.addPromiseListeners(promise, new EventEmitter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,8 +158,7 @@ export class ContentAuth extends AlfrescoApiClient {
|
||||
);
|
||||
});
|
||||
|
||||
ee(promise); // jshint ignore:line
|
||||
return promise;
|
||||
return this.addPromiseListeners(promise, new EventEmitter());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import ee from 'event-emitter';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { AlfrescoApiClient } from '../alfrescoApiClient';
|
||||
import { AlfrescoApiConfig } from '../alfrescoApiConfig';
|
||||
import { Authentication } from './authentication';
|
||||
@@ -604,7 +604,7 @@ export class Oauth2Auth extends AlfrescoApiClient {
|
||||
}
|
||||
);
|
||||
|
||||
ee(promise); // jshint ignore:line
|
||||
return this.addPromiseListeners(promise, new EventEmitter());
|
||||
}
|
||||
|
||||
pollingRefreshToken() {
|
||||
@@ -654,9 +654,7 @@ export class Oauth2Auth extends AlfrescoApiClient {
|
||||
);
|
||||
});
|
||||
|
||||
ee(promise); // jshint ignore:line
|
||||
|
||||
return promise;
|
||||
return this.addPromiseListeners(promise, new EventEmitter());
|
||||
}
|
||||
|
||||
universalBtoa(stringToConvert: string) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import ee from 'event-emitter';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { AlfrescoApiClient, AlfrescoApiClientPromise } from '../alfrescoApiClient';
|
||||
import { AlfrescoApiConfig } from '../alfrescoApiConfig';
|
||||
import { Authentication } from './authentication';
|
||||
@@ -123,8 +123,7 @@ export class ProcessAuth extends AlfrescoApiClient {
|
||||
);
|
||||
});
|
||||
|
||||
ee(promise); // jshint ignore:line
|
||||
return promise;
|
||||
return this.addPromiseListeners(promise, new EventEmitter());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,8 +153,7 @@ export class ProcessAuth extends AlfrescoApiClient {
|
||||
);
|
||||
});
|
||||
|
||||
ee(promise);
|
||||
return promise;
|
||||
return this.addPromiseListeners(promise, new EventEmitter());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import ee, { Emitter } from 'event-emitter';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import superagent, { Response, SuperAgentRequest } from 'superagent';
|
||||
import { Authentication } from './authentication/authentication';
|
||||
import { RequestOptions, HttpClient, SecurityOptions, Emitters } from './api-clients/http-client.interface';
|
||||
@@ -26,6 +26,8 @@ import { isBrowser, paramToString } from './utils';
|
||||
declare const Blob: any;
|
||||
declare const Buffer: any;
|
||||
|
||||
type EventEmitterInstance = InstanceType<typeof EventEmitter>;
|
||||
|
||||
const isProgressEvent = (event: ProgressEvent | unknown): event is ProgressEvent => (event as ProgressEvent)?.lengthComputable;
|
||||
|
||||
export class SuperagentHttpClient implements HttpClient {
|
||||
@@ -134,7 +136,7 @@ export class SuperagentHttpClient implements HttpClient {
|
||||
contentType: string,
|
||||
accept: string,
|
||||
responseType: string,
|
||||
eventEmitter: ee.Emitter,
|
||||
eventEmitter: EventEmitterInstance,
|
||||
returnType: string,
|
||||
securityOptions: SecurityOptions
|
||||
) {
|
||||
@@ -268,7 +270,7 @@ export class SuperagentHttpClient implements HttpClient {
|
||||
}
|
||||
}
|
||||
|
||||
private progress(event: ProgressEvent | unknown, eventEmitter: Emitter): void {
|
||||
private progress(event: ProgressEvent | unknown, eventEmitter: EventEmitterInstance): void {
|
||||
if (isProgressEvent(event)) {
|
||||
const percent = Math.round((event.loaded / event.total) * 100);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@alfresco/adf-process-services-cloud",
|
||||
"description": "Alfresco ADF process services cloud",
|
||||
"version": "8.1.0",
|
||||
"version": "8.3.0",
|
||||
"author": "Hyland Software, Inc. and its affiliates",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,12 +21,13 @@
|
||||
"@angular/platform-browser": ">=14.1.3",
|
||||
"@angular/platform-browser-dynamic": ">=14.1.3",
|
||||
"@angular/router": ">=14.1.3",
|
||||
"@alfresco/js-api": ">=9.1.0",
|
||||
"@alfresco/adf-core": ">=8.1.0",
|
||||
"@alfresco/adf-content-services": ">=8.1.0",
|
||||
"@alfresco/js-api": ">=9.1.1",
|
||||
"@alfresco/adf-core": ">=8.2.0",
|
||||
"@alfresco/adf-content-services": ">=8.2.0",
|
||||
"@apollo/client": ">=3.7.2",
|
||||
"@ngx-translate/core": ">=14.0.0",
|
||||
"apollo-angular": ">=4.0.1"
|
||||
"apollo-angular": ">=4.0.1",
|
||||
"editorjs-html": "^4.0.5"
|
||||
},
|
||||
"keywords": [
|
||||
"process-services-cloud",
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ describe('AppListCloudComponent', () => {
|
||||
oauth2Auth: {
|
||||
callCustomApi: () => Promise.resolve(fakeApplicationInstance)
|
||||
},
|
||||
isEcmLoggedIn: () => false,
|
||||
isLoggedIn: () => false,
|
||||
reply: jasmine.createSpy('reply')
|
||||
};
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatDialogHarness } from '@angular/material/dialog/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { TranslateLoader, TranslateService, provideTranslateService } from '@ngx-translate/core';
|
||||
import { Observable, of, throwError } from 'rxjs';
|
||||
import { firstValueFrom, Observable, of, throwError } from 'rxjs';
|
||||
import {
|
||||
cloudFormMock,
|
||||
conditionalUploadWidgetsMock,
|
||||
@@ -69,7 +69,7 @@ const mockOauth2Auth: any = {
|
||||
oauth2Auth: {
|
||||
callCustomApi: () => Promise.resolve()
|
||||
},
|
||||
isEcmLoggedIn: jasmine.createSpy('isEcmLoggedIn'),
|
||||
isLoggedIn: jasmine.createSpy('isLoggedIn'),
|
||||
reply: jasmine.createSpy('reply')
|
||||
};
|
||||
|
||||
@@ -1210,7 +1210,7 @@ describe('FormCloudComponent', () => {
|
||||
formComponent.formCloudRepresentationJSON = new FormCloudRepresentation(JSON.parse(JSON.stringify(cloudFormMock)));
|
||||
const form = formComponent.parseForm(formComponent.formCloudRepresentationJSON);
|
||||
expect(formComponent.fieldValidators.length).toBe(1);
|
||||
expect(form.fieldValidators.length).toBe(10);
|
||||
expect(form.fieldValidators.length).toBe(11);
|
||||
});
|
||||
|
||||
describe('form validations', () => {
|
||||
@@ -1616,7 +1616,7 @@ describe('Multilingual Form', () => {
|
||||
formComponent.ngOnChanges({ appName: new SimpleChange(null, appName, true) });
|
||||
expect(formCloudService.getForm).toHaveBeenCalledWith(appName, formId, 1);
|
||||
|
||||
await translateService.use('fr').toPromise();
|
||||
await firstValueFrom(translateService.use('fr'));
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
@@ -1626,7 +1626,7 @@ describe('Multilingual Form', () => {
|
||||
expect(getLabelValue('dateField')).toEqual('Champ de date (D-M-YYYY)');
|
||||
expect(getLabelValue('amountField')).toEqual('Champ Montant');
|
||||
|
||||
await translateService.use('en').toPromise();
|
||||
await firstValueFrom(translateService.use('en'));
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
+17
-2
@@ -18,12 +18,14 @@
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DisplayRichTextWidgetComponent } from './display-rich-text.widget';
|
||||
import { DisplayRichTextWidgetComponent, RICH_TEXT_PARSER_TOKEN } from './display-rich-text.widget';
|
||||
import { RichTextParserService } from '../../../services/rich-text-parser.service';
|
||||
|
||||
describe('DisplayRichTextWidgetComponent', () => {
|
||||
let widget: DisplayRichTextWidgetComponent;
|
||||
let fixture: ComponentFixture<DisplayRichTextWidgetComponent>;
|
||||
let debugEl: DebugElement;
|
||||
let mockRichTextParserService: jasmine.SpyObj<RichTextParserService>;
|
||||
|
||||
const cssSelector = {
|
||||
parsedHTML: '.adf-display-rich-text-widget-parsed-html'
|
||||
@@ -79,8 +81,14 @@ describe('DisplayRichTextWidgetComponent', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockRichTextParserService = jasmine.createSpyObj('RichTextParserService', ['parse']);
|
||||
mockRichTextParserService.parse.and.returnValue(
|
||||
'<h1>Editor.js</h1><p class="ce-tune-alignment--left">Display some <font color="#ff1300">formatted</font> <mark class="cdx-marker">text</mark></p>'
|
||||
);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [DisplayRichTextWidgetComponent]
|
||||
imports: [DisplayRichTextWidgetComponent],
|
||||
providers: [{ provide: RICH_TEXT_PARSER_TOKEN, useValue: mockRichTextParserService }]
|
||||
});
|
||||
fixture = TestBed.createComponent(DisplayRichTextWidgetComponent);
|
||||
widget = fixture.componentInstance;
|
||||
@@ -88,6 +96,13 @@ describe('DisplayRichTextWidgetComponent', () => {
|
||||
widget.field = fakeFormField;
|
||||
});
|
||||
|
||||
it('should call RichTextParserService.parse() method', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(mockRichTextParserService.parse).toHaveBeenCalledWith(fakeFormField.value);
|
||||
expect(mockRichTextParserService.parse).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should parse editorjs data to html', async () => {
|
||||
const expectedHtml =
|
||||
'<h1>Editor.js</h1><p class="ce-tune-alignment--left">Display some <font color="#ff1300">formatted</font> <mark class="cdx-marker">text</mark></p>';
|
||||
|
||||
+14
-27
@@ -17,10 +17,14 @@
|
||||
|
||||
/* eslint-disable @angular-eslint/component-selector */
|
||||
|
||||
import { Component, OnInit, SecurityContext, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, inject, InjectionToken, OnInit, SecurityContext, ViewEncapsulation } from '@angular/core';
|
||||
import { WidgetComponent, FormService } from '@alfresco/adf-core';
|
||||
import edjsHTML from 'editorjs-html';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { RichTextParserService } from '../../../services/rich-text-parser.service';
|
||||
|
||||
export const RICH_TEXT_PARSER_TOKEN = new InjectionToken<RichTextParserService>('RichTextParserService', {
|
||||
factory: () => new RichTextParserService()
|
||||
});
|
||||
|
||||
@Component({
|
||||
selector: 'display-rich-text',
|
||||
@@ -40,39 +44,22 @@ import { DomSanitizer } from '@angular/platform-browser';
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class DisplayRichTextWidgetComponent extends WidgetComponent implements OnInit {
|
||||
parsedHTML: any;
|
||||
parsedHTML: string | Error;
|
||||
|
||||
private static readonly CUSTOM_PARSER = {
|
||||
header: (block: any): string => {
|
||||
const paragraphAlign = block.data.alignment || block.data.align || block.tunes?.anyTuneName?.alignment;
|
||||
if (typeof paragraphAlign !== 'undefined' && ['left', 'right', 'center'].includes(paragraphAlign)) {
|
||||
return `<h${block.data.level} class="ce-tune-alignment--${paragraphAlign}">${block.data.text}</h${block.data.level}>`;
|
||||
} else {
|
||||
return `<h${block.data.level}>${block.data.text}</h${block.data.level}>`;
|
||||
}
|
||||
},
|
||||
paragraph: (block: any): string => {
|
||||
const paragraphAlign = block.data.alignment || block.data.align || block.tunes?.anyTuneName?.alignment;
|
||||
private readonly richTextParserService = inject(RICH_TEXT_PARSER_TOKEN);
|
||||
private readonly sanitizer = inject(DomSanitizer);
|
||||
|
||||
if (typeof paragraphAlign !== 'undefined' && ['left', 'right', 'center', 'justify'].includes(paragraphAlign)) {
|
||||
return `<p class="ce-tune-alignment--${paragraphAlign}">${block.data.text}</p>`;
|
||||
} else {
|
||||
return `<p>${block.data.text}</p>`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constructor(public formService: FormService, private readonly sanitizer: DomSanitizer) {
|
||||
constructor(formService: FormService) {
|
||||
super(formService);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.parsedHTML = edjsHTML(DisplayRichTextWidgetComponent.CUSTOM_PARSER, { strict: true }).parse(this.field.value);
|
||||
this.parsedHTML = this.richTextParserService.parse(this.field.value);
|
||||
|
||||
if (!(this.parsedHTML instanceof Error)) {
|
||||
this.sanitizeHtmlContent();
|
||||
} else {
|
||||
if (this.parsedHTML instanceof Error) {
|
||||
throw this.parsedHTML;
|
||||
} else {
|
||||
this.sanitizeHtmlContent();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-6
@@ -20,7 +20,7 @@ import { NotificationService } from '@alfresco/adf-core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ContentNodeSelectorComponent, ContentNodeSelectorComponentData, NodeAction, AlfrescoApiService } from '@alfresco/adf-content-services';
|
||||
import { Node, NodeEntry, NodesApi } from '@alfresco/js-api';
|
||||
import { from, Observable, Subject, throwError } from 'rxjs';
|
||||
import { firstValueFrom, from, Observable, Subject, throwError } from 'rxjs';
|
||||
import { catchError, map, mapTo } from 'rxjs/operators';
|
||||
import { DestinationFolderPathModel } from '../models/form-cloud-representation.model';
|
||||
|
||||
@@ -36,7 +36,11 @@ export class ContentCloudNodeSelectorService {
|
||||
|
||||
sourceNodeNotFound = false;
|
||||
|
||||
constructor(private apiService: AlfrescoApiService, private notificationService: NotificationService, private dialog: MatDialog) {}
|
||||
constructor(
|
||||
private apiService: AlfrescoApiService,
|
||||
private notificationService: NotificationService,
|
||||
private dialog: MatDialog
|
||||
) {}
|
||||
|
||||
openUploadFileDialog(
|
||||
currentFolderId?: string,
|
||||
@@ -65,25 +69,25 @@ export class ContentCloudNodeSelectorService {
|
||||
async getNodeIdFromPath(destinationFolderPath: DestinationFolderPathModel): Promise<string> {
|
||||
if (destinationFolderPath.alias && destinationFolderPath.path) {
|
||||
try {
|
||||
return await this.getNodeId(destinationFolderPath.alias, destinationFolderPath.path).toPromise();
|
||||
return await firstValueFrom(this.getNodeId(destinationFolderPath.alias, destinationFolderPath.path));
|
||||
} catch {
|
||||
/*empty*/
|
||||
}
|
||||
}
|
||||
|
||||
return this.getNodeId(destinationFolderPath.alias).toPromise();
|
||||
return firstValueFrom(this.getNodeId(destinationFolderPath.alias));
|
||||
}
|
||||
|
||||
async getNodeIdFromFolderVariableValue(variableValue: string, defaultAlias?: string): Promise<string> {
|
||||
const isExistingNode = await this.isExistingNode(variableValue);
|
||||
return isExistingNode ? variableValue : this.getNodeId(defaultAlias).toPromise();
|
||||
return isExistingNode ? variableValue : firstValueFrom(this.getNodeId(defaultAlias));
|
||||
}
|
||||
|
||||
async isExistingNode(nodeId: string): Promise<boolean> {
|
||||
let isExistingNode = false;
|
||||
if (nodeId) {
|
||||
try {
|
||||
isExistingNode = await this.getNodeId(nodeId).pipe(mapTo(true)).toPromise();
|
||||
isExistingNode = await firstValueFrom(this.getNodeId(nodeId).pipe(mapTo(true)));
|
||||
} catch {
|
||||
/*empty*/
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { RichTextParserService } from './rich-text-parser.service';
|
||||
|
||||
describe('RichTextParserService', () => {
|
||||
it('should test CUSTOM_PARSER.header method works correctly', () => {
|
||||
const block = { data: { text: 'Test Header', level: 2, alignment: 'center' } };
|
||||
const result = (RichTextParserService as any).CUSTOM_PARSER.header(block);
|
||||
expect(result).toBe('<h2 class="ce-tune-alignment--center">Test Header</h2>');
|
||||
});
|
||||
|
||||
it('should test CUSTOM_PARSER.paragraph method works correctly', () => {
|
||||
const block = { data: { text: 'Test paragraph text', alignment: 'justify' } };
|
||||
const result = (RichTextParserService as any).CUSTOM_PARSER.paragraph(block);
|
||||
expect(result).toBe('<p class="ce-tune-alignment--justify">Test paragraph text</p>');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import edjsHTML from 'editorjs-html';
|
||||
import { Injectable } from '@angular/core';
|
||||
export interface OutputData {
|
||||
version?: string;
|
||||
time?: number;
|
||||
blocks: any[];
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class RichTextParserService {
|
||||
private static readonly CUSTOM_PARSER = {
|
||||
header: (block: any): string => {
|
||||
if (!block.data || !block.data.text || !block.data.level) {
|
||||
return '';
|
||||
}
|
||||
const paragraphAlign = block.data.alignment || block.data.align || block.tunes?.anyTuneName?.alignment;
|
||||
if (typeof paragraphAlign !== 'undefined' && ['left', 'right', 'center'].includes(paragraphAlign)) {
|
||||
return `<h${block.data.level} class="ce-tune-alignment--${paragraphAlign}">${block.data.text}</h${block.data.level}>`;
|
||||
} else {
|
||||
return `<h${block.data.level}>${block.data.text}</h${block.data.level}>`;
|
||||
}
|
||||
},
|
||||
paragraph: (block: any): string => {
|
||||
if (!block.data || !block.data.text) {
|
||||
return '';
|
||||
}
|
||||
const paragraphAlign = block.data.alignment || block.data.align || block.tunes?.anyTuneName?.alignment;
|
||||
|
||||
if (typeof paragraphAlign !== 'undefined' && ['left', 'right', 'center', 'justify'].includes(paragraphAlign)) {
|
||||
return `<p class="ce-tune-alignment--${paragraphAlign}">${block.data.text}</p>`;
|
||||
} else {
|
||||
return `<p>${block.data.text}</p>`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
parse(richText: OutputData): string | Error {
|
||||
return edjsHTML(RichTextParserService.CUSTOM_PARSER, { strict: true }).parse(richText);
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import { ReactiveFormsModule, UntypedFormControl } from '@angular/forms';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, filter, mergeMap, switchMap, tap } from 'rxjs/operators';
|
||||
import { ComponentSelectionMode } from '../../types';
|
||||
import { IdentityGroupModel } from '../models/identity-group.model';
|
||||
@@ -248,7 +248,7 @@ export class GroupCloudComponent implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
private async searchGroup(name: string): Promise<IdentityGroupModel> {
|
||||
return (await this.identityGroupService.search(name).toPromise())[0];
|
||||
return (await firstValueFrom(this.identityGroupService.search(name)))[0];
|
||||
}
|
||||
|
||||
private getPreselectedGroups(): IdentityGroupModel[] {
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
ViewChild,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, filter, mergeMap, switchMap, tap } from 'rxjs/operators';
|
||||
import { FullNamePipe, InitialUsernamePipe } from '@alfresco/adf-core';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
@@ -368,9 +368,13 @@ export class PeopleCloudComponent implements OnInit, OnChanges, AfterViewInit {
|
||||
for (const user of this.getPreselectedUsers()) {
|
||||
try {
|
||||
const validationResult = (
|
||||
await this.identityUserService
|
||||
.search(user.username, { roles: this.roles, withinApplication: this.appName, groups: this.groupsRestriction })
|
||||
.toPromise()
|
||||
await firstValueFrom(
|
||||
this.identityUserService.search(user.username, {
|
||||
roles: this.roles,
|
||||
withinApplication: this.appName,
|
||||
groups: this.groupsRestriction
|
||||
})
|
||||
)
|
||||
)[0];
|
||||
|
||||
if (!this.equalsUsers(user, validationResult)) {
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ describe('EditProcessFilterCloudComponent', () => {
|
||||
oauth2Auth: {
|
||||
callCustomApi: () => Promise.resolve(fakeApplicationInstance)
|
||||
},
|
||||
isEcmLoggedIn: () => false,
|
||||
isLoggedIn: () => false,
|
||||
reply: jasmine.createSpy('reply')
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ describe('ProcessFiltersCloudComponent', () => {
|
||||
provide: ProcessListCloudService,
|
||||
useValue: {
|
||||
getProcessCounter: () => of(10),
|
||||
getProcessListCounter: () => of(10)
|
||||
getProcessListCount: () => of(10)
|
||||
}
|
||||
},
|
||||
{ provide: ProcessFilterCloudService, useValue: ProcessFilterCloudServiceMock }
|
||||
|
||||
+1
-1
@@ -319,7 +319,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges {
|
||||
|
||||
private fetchProcessFilterCounter(filter: ProcessFilterCloudModel): Observable<number> {
|
||||
return this.searchApiMethod === 'POST'
|
||||
? this.processListCloudService.getProcessListCounter(new ProcessFilterCloudAdapter(filter))
|
||||
? this.processListCloudService.getProcessListCount(new ProcessFilterCloudAdapter(filter))
|
||||
: this.processListCloudService.getProcessCounter(filter.appName, filter.status);
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -64,4 +64,14 @@ describe('ProcessFilterCloudModel', () => {
|
||||
expect(model.startFrom).toEqual(startOfDay(date).toISOString());
|
||||
expect(model.startTo).toEqual(endOfDay(date).toISOString());
|
||||
});
|
||||
|
||||
it('should assign value to the excludeByProcessCategoryName if provided', () => {
|
||||
const model = new ProcessFilterCloudModel({ excludeByProcessCategoryName: 'test' });
|
||||
expect(model.excludeByProcessCategoryName).toBe('test');
|
||||
});
|
||||
|
||||
it('should assign null to the excludeByProcessCategoryName if value not provided', () => {
|
||||
const model = new ProcessFilterCloudModel({});
|
||||
expect(model.excludeByProcessCategoryName).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
+75
-71
@@ -24,31 +24,31 @@ import { ProcessVariableFilterModel } from '../../../models/process-variable-fil
|
||||
|
||||
export class ProcessFilterCloudModel {
|
||||
id: string;
|
||||
name: string;
|
||||
key: string;
|
||||
icon: string;
|
||||
index: number;
|
||||
appName: string;
|
||||
name: string | null;
|
||||
key: string | null;
|
||||
icon: string | null;
|
||||
index: number | null;
|
||||
appName: string | null;
|
||||
appVersion?: number | number[];
|
||||
parentId?: string;
|
||||
processName: string;
|
||||
processInstanceId: string;
|
||||
initiator: string;
|
||||
status: string;
|
||||
sort: string;
|
||||
order: string;
|
||||
processDefinitionId: string;
|
||||
processDefinitionName?: string;
|
||||
processDefinitionKey: string;
|
||||
lastModified: Date;
|
||||
lastModifiedTo: Date;
|
||||
lastModifiedFrom: Date;
|
||||
startedDate: Date;
|
||||
completedDateType: DateCloudFilterType;
|
||||
startedDateType: DateCloudFilterType;
|
||||
suspendedDateType: DateCloudFilterType;
|
||||
completedDate: Date;
|
||||
environmentId?: string;
|
||||
parentId: string;
|
||||
processName: string | null;
|
||||
processInstanceId: string | null;
|
||||
initiator: string | null;
|
||||
status: string | null;
|
||||
sort: string | null;
|
||||
order: string | null;
|
||||
processDefinitionId: string | null;
|
||||
processDefinitionName: string | null;
|
||||
processDefinitionKey: string | null;
|
||||
lastModified: Date | null;
|
||||
lastModifiedTo: Date | null;
|
||||
lastModifiedFrom: Date | null;
|
||||
startedDate: Date | null;
|
||||
completedDateType: DateCloudFilterType | null;
|
||||
startedDateType: DateCloudFilterType | null;
|
||||
suspendedDateType: DateCloudFilterType | null;
|
||||
completedDate: Date | null;
|
||||
environmentId: string | null;
|
||||
showCounter: boolean;
|
||||
|
||||
processDefinitionNames: string[] | null;
|
||||
@@ -58,60 +58,64 @@ export class ProcessFilterCloudModel {
|
||||
initiators: string[] | null;
|
||||
appVersions: string[] | null;
|
||||
statuses: string[] | null;
|
||||
excludeByProcessCategoryName: string | null;
|
||||
|
||||
processVariableFilters?: ProcessVariableFilterModel[];
|
||||
|
||||
private dateRangeFilterService = new DateRangeFilterService();
|
||||
private _completedFrom: string;
|
||||
private _completedTo: string;
|
||||
private _startFrom: string;
|
||||
private _startTo: string;
|
||||
private _suspendedFrom: string;
|
||||
private _suspendedTo: string;
|
||||
private _completedFrom: string | null;
|
||||
private _completedTo: string | null;
|
||||
private _startFrom: string | null;
|
||||
private _startTo: string | null;
|
||||
private _suspendedFrom: string | null;
|
||||
private _suspendedTo: string | null;
|
||||
|
||||
constructor(obj?: any) {
|
||||
if (obj) {
|
||||
this.id = obj.id || Math.random().toString(36).substring(2, 9);
|
||||
this.name = obj.name || null;
|
||||
this.key = obj.key || null;
|
||||
this.environmentId = obj.environmentId;
|
||||
this.showCounter = obj.showCounter || false;
|
||||
this.icon = obj.icon || null;
|
||||
this.index = obj.index || null;
|
||||
this.appName = obj.appName || obj.appName === '' ? obj.appName : null;
|
||||
this.appVersion = obj.appVersion || null;
|
||||
|
||||
if (obj.appVersionMultiple && Array.isArray(obj.appVersionMultiple)) {
|
||||
this.appVersion = obj.appVersionMultiple;
|
||||
}
|
||||
|
||||
this.processInstanceId = obj.processInstanceId || null;
|
||||
this.parentId = obj.parentId || '';
|
||||
this.processName = obj.processName || null;
|
||||
this.initiator = obj.initiator || null;
|
||||
this.status = obj.status || null;
|
||||
this.sort = obj.sort || null;
|
||||
this.order = obj.order || null;
|
||||
this.processDefinitionId = obj.processDefinitionId || null;
|
||||
this.processDefinitionName = obj.processDefinitionName || null;
|
||||
this.processDefinitionKey = obj.processDefinitionKey || null;
|
||||
this.lastModified = obj.lastModified || null;
|
||||
this.lastModifiedTo = obj.lastModifiedTo || null;
|
||||
this.lastModifiedFrom = obj.lastModifiedFrom || null;
|
||||
this.startedDate = obj.startedDate || null;
|
||||
this.startFrom = obj._startFrom || null;
|
||||
this.startTo = obj._startTo || null;
|
||||
this.completedDateType = obj.completedDateType || null;
|
||||
this.startedDateType = obj.startedDateType || null;
|
||||
this.suspendedDateType = obj.suspendedDateType || null;
|
||||
this.completedFrom = obj._completedFrom || null;
|
||||
this.completedTo = obj._completedTo || null;
|
||||
this.completedDate = obj.completedDate || null;
|
||||
this._suspendedFrom = obj._suspendedFrom || null;
|
||||
this._suspendedTo = obj._suspendedTo || null;
|
||||
|
||||
this.initArrayProperties(obj);
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.id = obj.id || Math.random().toString(36).substring(2, 9);
|
||||
this.name = obj.name || null;
|
||||
this.key = obj.key || null;
|
||||
this.environmentId = obj.environmentId || null;
|
||||
this.showCounter = obj.showCounter || false;
|
||||
this.icon = obj.icon || null;
|
||||
this.index = obj.index || null;
|
||||
this.appName = obj.appName || obj.appName === '' ? obj.appName : null;
|
||||
this.appVersion = obj.appVersion || null;
|
||||
|
||||
if (obj.appVersionMultiple && Array.isArray(obj.appVersionMultiple)) {
|
||||
this.appVersion = obj.appVersionMultiple;
|
||||
}
|
||||
|
||||
this.processInstanceId = obj.processInstanceId || null;
|
||||
this.parentId = obj.parentId || '';
|
||||
this.processName = obj.processName || null;
|
||||
this.initiator = obj.initiator || null;
|
||||
this.status = obj.status || null;
|
||||
this.sort = obj.sort || null;
|
||||
this.order = obj.order || null;
|
||||
this.processDefinitionId = obj.processDefinitionId || null;
|
||||
this.processDefinitionName = obj.processDefinitionName || null;
|
||||
this.processDefinitionKey = obj.processDefinitionKey || null;
|
||||
this.lastModified = obj.lastModified || null;
|
||||
this.lastModifiedTo = obj.lastModifiedTo || null;
|
||||
this.lastModifiedFrom = obj.lastModifiedFrom || null;
|
||||
this.startedDate = obj.startedDate || null;
|
||||
this.startFrom = obj._startFrom || null;
|
||||
this.startTo = obj._startTo || null;
|
||||
this.completedDateType = obj.completedDateType || null;
|
||||
this.startedDateType = obj.startedDateType || null;
|
||||
this.suspendedDateType = obj.suspendedDateType || null;
|
||||
this.completedFrom = obj._completedFrom || null;
|
||||
this.completedTo = obj._completedTo || null;
|
||||
this.completedDate = obj.completedDate || null;
|
||||
this.excludeByProcessCategoryName = obj.excludeByProcessCategoryName || null;
|
||||
this._suspendedFrom = obj._suspendedFrom || null;
|
||||
this._suspendedTo = obj._suspendedTo || null;
|
||||
|
||||
this.initArrayProperties(obj);
|
||||
}
|
||||
|
||||
private initArrayProperties(obj) {
|
||||
|
||||
+1
-1
@@ -246,7 +246,7 @@ describe('ProcessFilterCloudService', () => {
|
||||
changedFilter.processDefinitionKey = 'modifiedProcessDefinitionKey';
|
||||
spyOn<any>(service, 'defaultProcessFilters').and.returnValue(fakeProcessCloudFilters);
|
||||
|
||||
await service.resetProcessFilterToDefaults('mock-appName', changedFilter).toPromise();
|
||||
await firstValueFrom(service.resetProcessFilterToDefaults('mock-appName', changedFilter));
|
||||
|
||||
expect(updatePreferenceSpy).toHaveBeenCalledWith('mock-appName', 'process-filters-mock-appName-mock-username', fakeProcessCloudFilters);
|
||||
});
|
||||
|
||||
+4
-4
@@ -111,7 +111,7 @@ describe('ProcessHeaderCloudComponent', () => {
|
||||
await fixture.whenStable();
|
||||
|
||||
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-startDate"] .adf-property-value'));
|
||||
expect(valueEl.nativeElement.value.trim()).toBe('Mar 9, 2019');
|
||||
expect(valueEl.nativeElement.innerText.trim()).toBe('Mar 9, 2019');
|
||||
});
|
||||
|
||||
it('should display lastModified date', async () => {
|
||||
@@ -121,7 +121,7 @@ describe('ProcessHeaderCloudComponent', () => {
|
||||
await fixture.whenStable();
|
||||
|
||||
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-lastModified"] .adf-property-value'));
|
||||
expect(valueEl.nativeElement.value.trim()).toBe('Mar 9, 2019');
|
||||
expect(valueEl.nativeElement.innerText.trim()).toBe('Mar 9, 2019');
|
||||
});
|
||||
|
||||
it('should display parentId', async () => {
|
||||
@@ -216,8 +216,8 @@ describe('ProcessHeaderCloudComponent', () => {
|
||||
const lastModifiedElement = fixture.debugElement.query(By.css('[data-automation-id="header-lastModified"] .adf-property-value'));
|
||||
|
||||
expect(component.dateFormat).toEqual('full');
|
||||
expect(startedDateElement.nativeElement.value.trim()).toBe('Saturday, March 9, 2019 at 12:00:00 AM GMT+00:00');
|
||||
expect(lastModifiedElement.nativeElement.value.trim()).toBe('Saturday, March 9, 2019 at 12:00:00 AM GMT+00:00');
|
||||
expect(startedDateElement.nativeElement.innerText.trim()).toBe('Saturday, March 9, 2019 at 12:00:00 AM GMT+00:00');
|
||||
expect(lastModifiedElement.nativeElement.innerText.trim()).toBe('Saturday, March 9, 2019 at 12:00:00 AM GMT+00:00');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+27
-5
@@ -374,7 +374,7 @@ describe('ProcessListCloudComponent', () => {
|
||||
expect(component.requestNode.appVersion).toEqual('1,2,3');
|
||||
});
|
||||
|
||||
it('should the payload NOT contain any app version when appVersion does not have a value', () => {
|
||||
it('should the payload NOT contain any app version when appVersion does NOT have a value', () => {
|
||||
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
||||
component.appVersion = undefined;
|
||||
component.ngAfterContentInit();
|
||||
@@ -476,7 +476,7 @@ describe('ProcessListCloudComponent', () => {
|
||||
expect(displayedColumns.length).toBe(2, 'only column with isHidden set to false and action column should be shown');
|
||||
});
|
||||
|
||||
it('should NOT request process variable if columns for process variables are not displayed', () => {
|
||||
it('should NOT request process variable if columns for process variables are NOT displayed', () => {
|
||||
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
||||
spyOn(preferencesService, 'getPreferences').and.returnValue(
|
||||
of({
|
||||
@@ -668,7 +668,7 @@ describe('ProcessListCloudComponent', () => {
|
||||
expect(component.processListRequestNode.appVersion).toEqual(['1', '2', '3']);
|
||||
});
|
||||
|
||||
it('should the payload NOT contain any app version when appVersion does not have a value', () => {
|
||||
it('should the payload NOT contain any app version when appVersion does NOT have a value', () => {
|
||||
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||
component.appVersion = undefined;
|
||||
component.ngAfterContentInit();
|
||||
@@ -752,7 +752,7 @@ describe('ProcessListCloudComponent', () => {
|
||||
expect(displayedColumns.length).toBe(2, 'only column with isHidden set to false and action column should be shown');
|
||||
});
|
||||
|
||||
it('should NOT request process variable if columns for process variables are not displayed', () => {
|
||||
it('should NOT request process variable if columns for process variables are NOT displayed', () => {
|
||||
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||
spyOn(preferencesService, 'getPreferences').and.returnValue(
|
||||
of({
|
||||
@@ -846,6 +846,28 @@ describe('ProcessListCloudComponent', () => {
|
||||
expect(fetchProcessListSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should reload process list when excludeByProcessCategoryName changes', () => {
|
||||
const fetchProcessListSpy = spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||
|
||||
fixture.componentRef.setInput('excludeByProcessCategoryName', 'mock-category');
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentRef.setInput('excludeByProcessCategoryName', 'mock-category-2');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fetchProcessListSpy).toHaveBeenCalledTimes(2);
|
||||
expect(fetchProcessListSpy).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
excludeByProcessCategoryName: 'mock-category'
|
||||
})
|
||||
);
|
||||
expect(fetchProcessListSpy).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
excludeByProcessCategoryName: 'mock-category-2'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should reload process list when sorting on a column changes', () => {
|
||||
const fetchProcessListSpy = spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||
component.onSortingChanged(
|
||||
@@ -945,7 +967,7 @@ describe('ProcessListCloudComponent', () => {
|
||||
expect(component.columns.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should not shown columns selector by default', () => {
|
||||
it('should NOT shown columns selector by default', () => {
|
||||
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
||||
|
||||
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
||||
|
||||
+4
-1
@@ -290,6 +290,8 @@ export class ProcessListCloudComponent
|
||||
@Input()
|
||||
processVariables: ProcessVariableFilterModel[];
|
||||
|
||||
@Input() excludeByProcessCategoryName: string = '';
|
||||
|
||||
/** Emitted when a row in the process list is clicked. */
|
||||
@Output()
|
||||
rowClick: EventEmitter<string> = new EventEmitter<string>();
|
||||
@@ -607,7 +609,8 @@ export class ProcessListCloudComponent
|
||||
suspendedFrom: this.suspendedFrom,
|
||||
suspendedTo: this.suspendedTo,
|
||||
processVariableKeys: this.getVariableDefinitionsRequestModel(),
|
||||
processVariableFilters: this.processVariables
|
||||
processVariableFilters: this.processVariables,
|
||||
excludeByProcessCategoryName: this.excludeByProcessCategoryName
|
||||
};
|
||||
|
||||
return new ProcessListRequestModel(requestNode);
|
||||
|
||||
+2
@@ -102,6 +102,7 @@ export class ProcessListRequestModel {
|
||||
completedTo?: string;
|
||||
suspendedFrom?: string;
|
||||
suspendedTo?: string;
|
||||
excludeByProcessCategoryName?: string;
|
||||
|
||||
processVariableFilters?: ProcessVariableFilterModel[];
|
||||
processVariableKeys?: string[];
|
||||
@@ -132,6 +133,7 @@ export class ProcessListRequestModel {
|
||||
this.suspendedTo = obj.suspendedTo;
|
||||
this.processVariableKeys = obj.processVariableKeys;
|
||||
this.processVariableFilters = obj.processVariableFilters;
|
||||
this.excludeByProcessCategoryName = obj.excludeByProcessCategoryName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+71
-25
@@ -115,19 +115,32 @@ describe('ProcessListCloudService', () => {
|
||||
});
|
||||
|
||||
describe('fetchProcessList', () => {
|
||||
it('should append to the call all the parameters', async () => {
|
||||
it('should append to the call all the query parameters', async () => {
|
||||
const processRequest = {
|
||||
appName: 'fakeName',
|
||||
pagination: { skipCount: 0, maxItems: 20 }
|
||||
pagination: { skipCount: 0, maxItems: 20 },
|
||||
parentId: ['fakeParentId'],
|
||||
excludeByProcessCategoryName: 'fakeCategory'
|
||||
} as ProcessListRequestModel;
|
||||
requestSpy.and.callFake(returnCallQueryParameters);
|
||||
|
||||
const res = await firstValueFrom(service.fetchProcessList(processRequest));
|
||||
const requestQueryParams = await firstValueFrom(service.fetchProcessList(processRequest));
|
||||
|
||||
expect(res).toBeDefined();
|
||||
expect(res).not.toBeNull();
|
||||
expect(res.skipCount).toBe(0);
|
||||
expect(res.maxItems).toBe(20);
|
||||
expect(requestQueryParams).toEqual({ skipCount: 0, maxItems: 20 });
|
||||
});
|
||||
|
||||
it('should append to the call all the body parameters', async () => {
|
||||
const processRequest = {
|
||||
appName: 'fakeName',
|
||||
pagination: { skipCount: 0, maxItems: 20 },
|
||||
parentId: ['fakeParentId'],
|
||||
excludeByProcessCategoryName: 'fakeCategory'
|
||||
} as ProcessListRequestModel;
|
||||
requestSpy.and.callFake(returnCallBody);
|
||||
|
||||
const requestBodyParams = await firstValueFrom(service.fetchProcessList(processRequest));
|
||||
|
||||
expect(requestBodyParams).toEqual({ excludeByProcessCategoryName: 'fakeCategory', parentId: ['fakeParentId'] });
|
||||
});
|
||||
|
||||
it('should concat the app name to the request url', async () => {
|
||||
@@ -137,34 +150,31 @@ describe('ProcessListCloudService', () => {
|
||||
} as ProcessListRequestModel;
|
||||
requestSpy.and.callFake(returnCallUrl);
|
||||
|
||||
const res = await firstValueFrom(service.fetchProcessList(processRequest));
|
||||
const requestUrl = await firstValueFrom(service.fetchProcessList(processRequest));
|
||||
|
||||
expect(res).toBeDefined();
|
||||
expect(res).not.toBeNull();
|
||||
expect(res).toContain('/fakeName/query/v1/process-instances/search');
|
||||
expect(requestUrl).toContain('/fakeName/query/v1/process-instances/search');
|
||||
});
|
||||
|
||||
it('should concat the sorting to append as parameters', async () => {
|
||||
it('should concat the sorting to append as a body sort parameter', async () => {
|
||||
const processRequest = {
|
||||
appName: 'fakeName',
|
||||
pagination: { skipCount: 0, maxItems: 20 },
|
||||
sorting: { orderBy: 'NAME', direction: 'DESC', isFieldProcessVariable: false }
|
||||
} as ProcessListRequestModel;
|
||||
requestSpy.and.callFake(returnCallQueryParameters);
|
||||
requestSpy.and.callFake(returnCallBody);
|
||||
|
||||
const res = await firstValueFrom(service.fetchProcessList(processRequest));
|
||||
const requestBodyParams = await firstValueFrom(service.fetchProcessList(processRequest));
|
||||
|
||||
expect(res).toBeDefined();
|
||||
expect(res).not.toBeNull();
|
||||
expect(requestBodyParams).toEqual({ sort: { field: 'NAME', direction: 'desc', isProcessVariable: false } });
|
||||
});
|
||||
|
||||
it('should return an error when app name is not specified', async () => {
|
||||
const taskRequest = { appName: null } as ProcessListRequestModel;
|
||||
requestSpy.and.callFake(returnCallUrl);
|
||||
|
||||
const res = await firstValueFrom(service.fetchProcessList(taskRequest).pipe(catchError((error) => of(error.message))));
|
||||
const error = await firstValueFrom(service.fetchProcessList(taskRequest).pipe(catchError((error) => of(error.message))));
|
||||
|
||||
expect(res).toBe('Appname not configured');
|
||||
expect(error).toBe('Appname not configured');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -172,7 +182,7 @@ describe('ProcessListCloudService', () => {
|
||||
it('should append to the call all the parameters', async () => {
|
||||
const processRequest = { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service' } as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallQueryParameters);
|
||||
const request = await service.getAdminProcessByRequest(processRequest).toPromise();
|
||||
const request = await firstValueFrom(service.getAdminProcessByRequest(processRequest));
|
||||
|
||||
expect(request).toBeDefined();
|
||||
expect(request).not.toBeNull();
|
||||
@@ -184,7 +194,7 @@ describe('ProcessListCloudService', () => {
|
||||
it('should concat the app name to the request url', async () => {
|
||||
const processRequest = { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service' } as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallUrl);
|
||||
const requestUrl = await service.getAdminProcessByRequest(processRequest).toPromise();
|
||||
const requestUrl = await firstValueFrom(service.getAdminProcessByRequest(processRequest));
|
||||
|
||||
expect(requestUrl).toBeDefined();
|
||||
expect(requestUrl).not.toBeNull();
|
||||
@@ -203,7 +213,7 @@ describe('ProcessListCloudService', () => {
|
||||
]
|
||||
} as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallQueryParameters);
|
||||
const request = await service.getAdminProcessByRequest(processRequest).toPromise();
|
||||
const request = await firstValueFrom(service.getAdminProcessByRequest(processRequest));
|
||||
|
||||
expect(request).toBeDefined();
|
||||
expect(request).not.toBeNull();
|
||||
@@ -215,7 +225,7 @@ describe('ProcessListCloudService', () => {
|
||||
requestSpy.and.callFake(returnCallUrl);
|
||||
|
||||
try {
|
||||
await service.getAdminProcessByRequest(processRequest).toPromise();
|
||||
await firstValueFrom(service.getAdminProcessByRequest(processRequest));
|
||||
|
||||
fail('Should have thrown error');
|
||||
} catch (error) {
|
||||
@@ -226,7 +236,7 @@ describe('ProcessListCloudService', () => {
|
||||
it('should make post request', async () => {
|
||||
const processRequest = { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service' } as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallOperation);
|
||||
const adminProcessResponse = await service.getAdminProcessByRequest(processRequest).toPromise();
|
||||
const adminProcessResponse = await firstValueFrom(service.getAdminProcessByRequest(processRequest));
|
||||
expect(adminProcessResponse).toBeDefined();
|
||||
expect(adminProcessResponse).not.toBeNull();
|
||||
expect(adminProcessResponse.httpMethod).toBe('POST');
|
||||
@@ -241,7 +251,7 @@ describe('ProcessListCloudService', () => {
|
||||
variableKeys: ['test-one', 'test-two']
|
||||
} as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallQueryParameters);
|
||||
const requestParams = await service.getAdminProcessByRequest(processRequest).toPromise();
|
||||
const requestParams = await firstValueFrom(service.getAdminProcessByRequest(processRequest));
|
||||
|
||||
expect(requestParams).toBeDefined();
|
||||
expect(requestParams).not.toBeNull();
|
||||
@@ -257,7 +267,7 @@ describe('ProcessListCloudService', () => {
|
||||
variableKeys: ['test-one', 'test-two']
|
||||
} as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallBody);
|
||||
const requestBodyParams = await service.getAdminProcessByRequest(processRequest).toPromise();
|
||||
const requestBodyParams = await firstValueFrom(service.getAdminProcessByRequest(processRequest));
|
||||
|
||||
expect(requestBodyParams).toBeDefined();
|
||||
expect(requestBodyParams).not.toBeNull();
|
||||
@@ -267,4 +277,40 @@ describe('ProcessListCloudService', () => {
|
||||
expect(requestBodyParams.variableKeys[1]).toBe('test-two');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getProcessListCount', () => {
|
||||
it('should concat the app name to the request url', async () => {
|
||||
const taskRequest = {
|
||||
appName: 'fakeName'
|
||||
} as ProcessListRequestModel;
|
||||
requestSpy.and.callFake(returnCallUrl);
|
||||
|
||||
const res = await firstValueFrom(service.getProcessListCount(taskRequest));
|
||||
|
||||
expect(res).toBeDefined();
|
||||
expect(res).not.toBeNull();
|
||||
expect(res).toContain('fakeName/query/v1/process-instances/count');
|
||||
});
|
||||
|
||||
it('should return 0 if response is falsy for getProcessListCount', async () => {
|
||||
const taskRequest = {
|
||||
appName: 'fakeName',
|
||||
pagination: { skipCount: 0, maxItems: 20 }
|
||||
} as ProcessListRequestModel;
|
||||
requestSpy.and.callFake(() => Promise.resolve(null));
|
||||
|
||||
const res = await firstValueFrom(service.getProcessListCount(taskRequest));
|
||||
|
||||
expect(res).toBe(0);
|
||||
});
|
||||
|
||||
it('should throw error if appName is not configured in getProcessListCount', async () => {
|
||||
const taskRequest = { appName: null } as ProcessListRequestModel;
|
||||
requestSpy.and.callFake(returnCallUrl);
|
||||
|
||||
const res = await firstValueFrom(service.getProcessListCount(taskRequest).pipe(catchError((error) => of(error.message))));
|
||||
|
||||
expect(res).toBe('Appname not configured');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+15
-3
@@ -100,8 +100,8 @@ export class ProcessListCloudService extends BaseCloudService {
|
||||
);
|
||||
}
|
||||
|
||||
protected buildQueryData(requestNode: ProcessListRequestModel) {
|
||||
const queryData: any = {
|
||||
protected buildQueryData(requestNode: ProcessListRequestModel): { [key: string]: any } {
|
||||
const queryData: { [key: string]: any } = {
|
||||
name: requestNode.name,
|
||||
id: requestNode.id,
|
||||
parentId: requestNode.parentId,
|
||||
@@ -118,7 +118,8 @@ export class ProcessListCloudService extends BaseCloudService {
|
||||
suspendedFrom: requestNode.suspendedFrom,
|
||||
suspendedTo: requestNode.suspendedTo,
|
||||
processVariableKeys: requestNode.processVariableKeys,
|
||||
processVariableFilters: requestNode.processVariableFilters
|
||||
processVariableFilters: requestNode.processVariableFilters,
|
||||
excludeByProcessCategoryName: requestNode.excludeByProcessCategoryName
|
||||
};
|
||||
|
||||
if (requestNode.sorting) {
|
||||
@@ -229,6 +230,17 @@ export class ProcessListCloudService extends BaseCloudService {
|
||||
return this.getProcess(callback, defaultQueryUrl, requestNode, queryUrl);
|
||||
}
|
||||
|
||||
getProcessListCount(requestNode: ProcessListRequestModel): Observable<number> {
|
||||
if (!requestNode?.appName) {
|
||||
return throwError(() => new Error('Appname not configured'));
|
||||
}
|
||||
|
||||
const queryUrl = `${this.getBasePath(requestNode.appName)}/query/v1/process-instances/count`;
|
||||
const queryData = this.buildQueryData(requestNode);
|
||||
|
||||
return this.post<object, number>(queryUrl, queryData).pipe(map((response) => response || 0));
|
||||
}
|
||||
|
||||
private getVariableKeysFromQueryParams(queryParams: any): string[] {
|
||||
if (!queryParams['variableKeys'] || queryParams['variableKeys'].length <= 0) {
|
||||
return [];
|
||||
|
||||
+16
-22
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { of, throwError } from 'rxjs';
|
||||
import { firstValueFrom, of, throwError } from 'rxjs';
|
||||
import { StartProcessCloudService } from './start-process-cloud.service';
|
||||
import { fakeProcessPayload } from '../mock/start-process.component.mock';
|
||||
import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model';
|
||||
@@ -37,7 +37,7 @@ describe('StartProcessCloudService', () => {
|
||||
|
||||
it('should be able to create a new process', async () => {
|
||||
spyOn(service, 'startProcess').and.returnValue(of({ id: 'fake-id', name: 'fake-name' }));
|
||||
const result = await service.startProcess('appName1', fakeProcessPayload).toPromise();
|
||||
const result = await firstValueFrom(service.startProcess('appName1', fakeProcessPayload));
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.id).toEqual('fake-id');
|
||||
@@ -46,7 +46,7 @@ describe('StartProcessCloudService', () => {
|
||||
|
||||
it('should be able to create a new process with form', async () => {
|
||||
spyOn(service, 'startProcessWithForm').and.returnValue(of({ id: 'fake-id', name: 'fake-name' }));
|
||||
const result = await service.startProcessWithForm('appName1', 'mockFormId', 1, fakeProcessPayload).toPromise();
|
||||
const result = await firstValueFrom(service.startProcessWithForm('appName1', 'mockFormId', 1, fakeProcessPayload));
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.id).toEqual('fake-id');
|
||||
@@ -61,14 +61,11 @@ describe('StartProcessCloudService', () => {
|
||||
});
|
||||
|
||||
spyOn(service, 'startProcess').and.returnValue(throwError(errorResponse));
|
||||
const result = await service
|
||||
.startProcess('appName1', fakeProcessPayload)
|
||||
.toPromise()
|
||||
.catch((error) => {
|
||||
expect(error.status).toEqual(404);
|
||||
expect(error.statusText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
});
|
||||
const result = await firstValueFrom(service.startProcess('appName1', fakeProcessPayload)).catch((error) => {
|
||||
expect(error.status).toEqual(404);
|
||||
expect(error.statusText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
});
|
||||
|
||||
if (result) {
|
||||
fail('expected an error, not applications');
|
||||
@@ -77,7 +74,7 @@ describe('StartProcessCloudService', () => {
|
||||
|
||||
it('should be able to get all the process definitions', async () => {
|
||||
spyOn(service, 'getProcessDefinitions').and.returnValue(of([new ProcessDefinitionCloud({ id: 'fake-id', name: 'fake-name' })]));
|
||||
const result = await service.getProcessDefinitions('appName1').toPromise();
|
||||
const result = await firstValueFrom(service.getProcessDefinitions('appName1'));
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result[0].id).toEqual('fake-id');
|
||||
@@ -91,14 +88,11 @@ describe('StartProcessCloudService', () => {
|
||||
statusText: 'Not Found'
|
||||
});
|
||||
spyOn(service, 'getProcessDefinitions').and.returnValue(throwError(errorResponse));
|
||||
const result = await service
|
||||
.getProcessDefinitions('appName1')
|
||||
.toPromise()
|
||||
.catch((error) => {
|
||||
expect(error.status).toEqual(404);
|
||||
expect(error.statusText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
});
|
||||
const result = await firstValueFrom(service.getProcessDefinitions('appName1')).catch((error) => {
|
||||
expect(error.status).toEqual(404);
|
||||
expect(error.statusText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
});
|
||||
|
||||
if (result) {
|
||||
fail('expected an error, not applications');
|
||||
@@ -111,7 +105,7 @@ describe('StartProcessCloudService', () => {
|
||||
const requestSpy = spyOn(adfHttpClient, 'request');
|
||||
requestSpy.and.returnValue(Promise.resolve({ static1: 'value', static2: 0, static3: true }));
|
||||
|
||||
const result = await service.getStartEventFormStaticValuesMapping(appName, processDefinitionId).toPromise();
|
||||
const result = await firstValueFrom(service.getStartEventFormStaticValuesMapping(appName, processDefinitionId));
|
||||
expect(result.length).toEqual(3);
|
||||
expect(result[0].name).toEqual('static1');
|
||||
expect(result[0].id).toEqual('static1');
|
||||
@@ -132,7 +126,7 @@ describe('StartProcessCloudService', () => {
|
||||
const requestSpy = spyOn(adfHttpClient, 'request');
|
||||
requestSpy.and.returnValue(Promise.resolve({ constant1: 'value', constant2: '0', constant3: 'true' }));
|
||||
|
||||
const result = await service.getStartEventConstants(appName, processDefinitionId).toPromise();
|
||||
const result = await firstValueFrom(service.getStartEventConstants(appName, processDefinitionId));
|
||||
|
||||
expect(result.length).toEqual(3);
|
||||
expect(result[0].name).toEqual('constant1');
|
||||
|
||||
+3
-3
@@ -43,7 +43,7 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
let fixture: ComponentFixture<TaskFiltersCloudComponent>;
|
||||
let getTaskFilterCounterSpy: jasmine.Spy;
|
||||
let getTaskListFiltersSpy: jasmine.Spy;
|
||||
let getTaskListCounterSpy: jasmine.Spy;
|
||||
let getTaskListCountSpy: jasmine.Spy;
|
||||
|
||||
const configureTestingModule = (searchApiMethod: 'GET' | 'POST') => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -53,7 +53,7 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
taskFilterService = TestBed.inject(TaskFilterCloudService);
|
||||
taskListService = TestBed.inject(TaskListCloudService);
|
||||
getTaskFilterCounterSpy = spyOn(taskFilterService, 'getTaskFilterCounter').and.returnValue(of(11));
|
||||
getTaskListCounterSpy = spyOn(taskListService, 'getTaskListCounter').and.returnValue(of(11));
|
||||
getTaskListCountSpy = spyOn(taskListService, 'getTaskListCount').and.returnValue(of(11));
|
||||
spyOn(taskFilterService, 'getTaskNotificationSubscription').and.returnValue(of(taskNotifications));
|
||||
getTaskListFiltersSpy = spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(of(fakeGlobalFilter));
|
||||
|
||||
@@ -357,7 +357,7 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
);
|
||||
await filterButton.click();
|
||||
|
||||
expect(getTaskListCounterSpy).toHaveBeenCalledWith(new TaskFilterCloudAdapter(fakeGlobalFilter[0]));
|
||||
expect(getTaskListCountSpy).toHaveBeenCalledWith(new TaskFilterCloudAdapter(fakeGlobalFilter[0]));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user