Compare commits

..
37 Commits
Author SHA1 Message Date
bsayan2 72c4395309 alfresco hackathon init 2025-09-10 16:40:22 +05:30
Wojciech Duda 09c35ea903 AAE-37573 Add editorjs-html to process-services-cloud dependencies (#11183) 2025-09-09 13:19:51 +02:00
Mykyta Maliarchuk 923feccdb9 [MNT-25274] Fixed issue while uploading files with multi-valued properties (#11182)
* [MNT-25274] Fixed issue while uploading files with multi-valued properties

* [MNT-25274] set a more specific type
2025-09-09 10:59:16 +02:00
dependabot[bot] e7024d1970 build(deps): bump Alfresco/alfresco-build-tools (#11175)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.33.0 to 8.33.1.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](https://github.com/alfresco/alfresco-build-tools/compare/ce7f6e0b62727da8600acf87d72ffbf064ac34b5...62d25370db3ca229de518fab5b32ec014a061101)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.33.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:24:47 -04:00
dependabot[bot] a72726427b build(deps): bump actions/setup-node in /.github/actions/setup (#11176)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.4.0 to 5.0.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/49933ea5288caeca8642d1e84afbd3f7d6820020...a0853c24544627f65ddf259abe73b1d18a591444)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:24:27 -04:00
dependabot[bot] 054ecf3dcb build(deps): bump github/codeql-action from 3.29.11 to 3.30.1 (#11177)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.11 to 3.30.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/3c3833e0f8c1c83d449a7478aa59c036a9165498...f1f6e5f6af878fb37288ce1c627459e94dbf7d01)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:24:07 -04:00
dependabot[bot] 8c51e783e5 build(deps): bump Alfresco/alfresco-build-tools from 8.33.0 to 8.33.1 (#11179)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.33.0 to 8.33.1.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](https://github.com/alfresco/alfresco-build-tools/compare/ce7f6e0b62727da8600acf87d72ffbf064ac34b5...62d25370db3ca229de518fab5b32ec014a061101)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.33.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:23:51 -04:00
dependabot[bot] fd88e1014c build(deps): bump actions/setup-node from 4.4.0 to 5.0.0 (#11180)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.4.0 to 5.0.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/49933ea5288caeca8642d1e84afbd3f7d6820020...a0853c24544627f65ddf259abe73b1d18a591444)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:23:33 -04:00
dependabot[bot] 2ec227c331 build(deps): bump actions/github-script from 7.0.1 to 8.0.0 (#11178)
Bumps [actions/github-script](https://github.com/actions/github-script) from 7.0.1 to 8.0.0.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/60a0d83039c74a4aee543508d2ffcb1c3799cdea...ed597411d8f924073f98dfc5c65a23a2325f34cd)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:22:42 -04:00
dependabot[bot] 2235c6b09e build(deps): bump crowdin/github-action from 2.10.0 to 2.11.0 (#11181)
Bumps [crowdin/github-action](https://github.com/crowdin/github-action) from 2.10.0 to 2.11.0.
- [Release notes](https://github.com/crowdin/github-action/releases)
- [Commits](https://github.com/crowdin/github-action/compare/9787f4fcb6a8450929673f1e8db841e8a5c35a2f...0749939f635900a2521aa6aac7a3766642b2dc71)

---
updated-dependencies:
- dependency-name: crowdin/github-action
  dependency-version: 2.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:21:45 -04:00
Ehsan Rezaei dfa6db447f AAE-37996 Updated Docker secrets (#11170) 2025-09-04 17:42:33 +02:00
Ehsan Rezaei e7bba32679 AAE-37368 Applying new tasks and process count APIs (#11119)
* AAE-37368 Applying new tasks and process count APIs

* AAE-37368 Added tasks API and unit tests
2025-09-04 16:56:09 +02:00
Wojciech Duda ea81fcd6ad AAE-37907 Move editorjs-html to dependencies (#11169) 2025-09-04 12:23:35 +01:00
Wojciech Duda f1fea0a70b AAE-37906 Extract rich text parsing service (#11164)
* AAE-37906 Extract rich text parsing service

* AAE-37906 Add unit tests

* AAE-37906 Remove unused import

* AAE-37906 Fix units
2025-09-03 18:02:34 +02:00
Vito Albano 0564e67ba6 AAE-37923 - npm-check-bundle should take the released version to be sure to check the correct package on patch release (#11168) 2025-09-03 15:15:21 +01:00
dependabot[bot] 3ac2a91ea0 build(deps-dev): bump @typescript-eslint/typescript-estree (#11157)
Bumps [@typescript-eslint/typescript-estree](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-estree) from 8.38.0 to 8.41.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-estree/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.41.0/packages/typescript-estree)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/typescript-estree"
  dependency-version: 8.41.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-03 08:55:44 -04:00
dominikiwanekhyland 88b857a8fe bump @nx/workspace from 20.8.0 to 20.8.2 (#11163) 2025-09-03 08:50:12 -04:00
Amedeo Lepore 51c55674b4 AAE-35340 Replace deprecated toPromise() usages with firstValueFrom / lastValueFrom (#11161) 2025-09-03 08:37:13 -04:00
dependabot[bot] 93c872b65d build(deps): bump Alfresco/alfresco-build-tools from 8.32.1 to 8.33.0 (#11159)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.32.1 to 8.33.0.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](https://github.com/alfresco/alfresco-build-tools/compare/b8c3a89596708e92238c2a16c8c659772ba7d0d8...ce7f6e0b62727da8600acf87d72ffbf064ac34b5)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-03 08:21:05 -04:00
dependabot[bot] 195194fb15 build(deps): bump koa and @nx/angular (#11166)
Bumps [koa](https://github.com/koajs/koa) to 3.0.1 and updates ancestor dependency [@nx/angular](https://github.com/nrwl/nx/tree/HEAD/packages/angular). These dependencies need to be updated together.


Updates `koa` from 2.16.1 to 3.0.1
- [Release notes](https://github.com/koajs/koa/releases)
- [Changelog](https://github.com/koajs/koa/blob/master/History.md)
- [Commits](https://github.com/koajs/koa/compare/v2.16.1...v3.0.1)

Updates `@nx/angular` from 21.2.2 to 21.4.1
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/21.4.1/packages/angular)

---
updated-dependencies:
- dependency-name: koa
  dependency-version: 3.0.1
  dependency-type: indirect
- dependency-name: "@nx/angular"
  dependency-version: 21.4.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-03 08:16:50 -04:00
Vito Albano 49375181e4 AAE-30878 Migrating from event-emitter to eventemitter3 which is … (#11116) 2025-09-02 08:33:35 -04:00
Bartosz Sekula aae4efdd92 AAE-30058 Integer form field should not allow more than 10 digits (#11146)
* AAE-30058 Integer form field should not allow more than 10 digits

* Update maxLength validator to get custom values

* fix units
2025-09-02 11:51:13 +02:00
Ehsan Rezaei a4e62974ea AAE-37847 Create Git tag at the end of release branch process when on patch PR (#11162)
* AAE-37847 Create Git tag at the end of release branch process when on patch PR

* AAE-37847 Removed extra if condition
2025-09-02 11:13:54 +02:00
Wiktor Danielewski 81fed833f5 AAE-35842 Admin - filter out form specific processes (in Process Instances tab) (#11131)
* AAE-35842 Update ProcessFilterCloudModel

* AAE-35842 Initial implementation
2025-09-02 09:10:08 +02:00
Pawel Solowczuk 609144551c AAE-34139 Switch Yes and No button places and change focus from No to Yes. (#11148) 2025-09-01 16:04:44 +02:00
Amedeo Lepore 5bc4c02a94 AAE-35385 Improve releasing-patches guide (#11149) 2025-09-01 15:25:21 +02:00
Grzegorz Jaśkowski 04d488b8ea [ACS-9768] remove deprecated methods from auth related components (#11031)
* [ACS-9768] remove deprecated methods from auth related components

* [ACS-9768] remove deprecated method, adjust unit tests

* [ACS-9768] fix sonar cloud issues

* [ACS-9768] fix auth mocks

* [ACS-9768] fix sonar cloud alerts

* [ACS-9768] update method name in docs
2025-09-01 11:28:41 +02:00
Ehsan Rezaei dabab63f78 Fix Post release version bump to align with ACA (#11153) 2025-08-29 14:24:59 +02:00
AleksanderSklorz b277a29f32 [ACS-4593] Fixed issue with too long notification text in snackbar (#11152) 2025-08-28 15:05:05 +02:00
MichalKinas bdeb8a8e51 [ACS-10101] Reduce cron job execution window (#11151) 2025-08-28 14:12:38 +02:00
dependabot[bot] 9c0313a00c build(deps): bump Alfresco/alfresco-build-tools (#11139)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.31.0 to 8.32.1.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](https://github.com/alfresco/alfresco-build-tools/compare/a32503e10c10dc999ffcdfa8bc8c5e705cf36928...b8c3a89596708e92238c2a16c8c659772ba7d0d8)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.32.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-28 08:27:11 +02:00
dependabot[bot] 2200135b82 build(deps): bump Alfresco/alfresco-build-tools from 8.31.0 to 8.32.1 (#11140)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.31.0 to 8.32.1.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](https://github.com/alfresco/alfresco-build-tools/compare/a32503e10c10dc999ffcdfa8bc8c5e705cf36928...b8c3a89596708e92238c2a16c8c659772ba7d0d8)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.32.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-27 11:45:14 -04:00
dependabot[bot] a141d19629 build(deps): bump crowdin/github-action from 2.9.1 to 2.10.0 (#11141)
Bumps [crowdin/github-action](https://github.com/crowdin/github-action) from 2.9.1 to 2.10.0.
- [Release notes](https://github.com/crowdin/github-action/releases)
- [Commits](https://github.com/crowdin/github-action/compare/590c05e09a29f392b203faf4d6aa8e0cd32c7835...9787f4fcb6a8450929673f1e8db841e8a5c35a2f)

---
updated-dependencies:
- dependency-name: crowdin/github-action
  dependency-version: 2.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-27 11:45:03 -04:00
dependabot[bot] 15ed9a5f81 build(deps): bump github/codeql-action from 3.29.9 to 3.29.11 (#11142)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.9 to 3.29.11.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/df559355d593797519d70b90fc8edd5db049e7a2...3c3833e0f8c1c83d449a7478aa59c036a9165498)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-27 11:44:51 -04:00
dominikiwanekhyland f205cbc33c Post release version bump (#11147) 2025-08-26 15:58:18 +02:00
dominikiwanekhyland e0f0486e66 Release 8.1.1 (#11145) 2025-08-26 11:35:21 +02:00
dominikiwanekhyland 0576aac985 Revert "CSX-73 Date and Select Satori (#11056)" (#11144)
This reverts commit 9b6688ce10.
2025-08-26 11:00:03 +02:00
116 changed files with 3398 additions and 2871 deletions
+3 -1
View File
@@ -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}
+2 -2
View File
@@ -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
+3 -3
View File
@@ -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
+3 -3
View File
@@ -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"
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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
+5 -5
View File
@@ -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
View File
@@ -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
+6 -6
View File
@@ -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()
);
}
}
+1
View File
@@ -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)
+50
View File
@@ -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) |
+1
View File
@@ -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)
+7
View File
@@ -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 &#34;CSX-73 Date and Select Satori (#11056)&#34; (#11144)
+18 -8
View File
@@ -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.
+1
View File
@@ -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)
+23
View File
@@ -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 |
| --- | --- | --- |
+6 -6
View File
@@ -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",
+2 -2
View File
@@ -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",
+3 -3
View File
@@ -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);
@@ -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>
@@ -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"]'));
@@ -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')
};
@@ -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) {
@@ -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();
});
+51 -34
View File
@@ -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);
}
+3 -3
View File
@@ -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);
});
});
});
@@ -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>
@@ -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');
@@ -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
@@ -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 -1
View File
@@ -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",
+2 -2
View File
@@ -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",
+3 -3
View File
@@ -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",
+2 -2
View File
@@ -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"
},
+21 -27
View File
@@ -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);
}
/**
+41 -31
View File
@@ -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 {
+4 -7
View File
@@ -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());
}
/**
+3 -5
View File
@@ -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) {
+3 -5
View File
@@ -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());
}
/**
+5 -3
View File
@@ -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);
+6 -5
View File
@@ -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",
@@ -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();
@@ -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>';
@@ -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();
}
}
@@ -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)) {
@@ -83,7 +83,7 @@ describe('EditProcessFilterCloudComponent', () => {
oauth2Auth: {
callCustomApi: () => Promise.resolve(fakeApplicationInstance)
},
isEcmLoggedIn: () => false,
isLoggedIn: () => false,
reply: jasmine.createSpy('reply')
};
@@ -51,7 +51,7 @@ describe('ProcessFiltersCloudComponent', () => {
provide: ProcessListCloudService,
useValue: {
getProcessCounter: () => of(10),
getProcessListCounter: () => of(10)
getProcessListCount: () => of(10)
}
},
{ provide: ProcessFilterCloudService, useValue: ProcessFilterCloudServiceMock }
@@ -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);
}
}
@@ -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();
});
});
@@ -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) {
@@ -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);
});
@@ -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');
});
});
});
@@ -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);
@@ -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);
@@ -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;
}
}
@@ -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');
});
});
});
@@ -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,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');
@@ -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