mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-16 18:13:06 +00:00
Compare commits
89
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3496363b8e | ||
|
|
d25038dc59 | ||
|
|
c348ea32c0 | ||
|
|
0505b1d5c3 | ||
|
|
0a19be090e | ||
|
|
b69c3ae1ae | ||
|
|
ce907417b7 | ||
|
|
4c7765e3fa | ||
|
|
407a64996e | ||
|
|
c0a102f87f | ||
|
|
066ef30516 | ||
|
|
01cea60490 | ||
|
|
f438c6ce69 | ||
|
|
09ef05038c | ||
|
|
c7bd56f244 | ||
|
|
b86e41515e | ||
|
|
0b35118dee | ||
|
|
fe2b3d85dd | ||
|
|
b0dc85422c | ||
|
|
23f0392c12 | ||
|
|
997303e6a7 | ||
|
|
45d1818f04 | ||
|
|
2f50af623d | ||
|
|
9c56712328 | ||
|
|
448e667c52 | ||
|
|
18e602aa85 | ||
|
|
53ef0cfc00 | ||
|
|
4cd8648a05 | ||
|
|
7f57b3936b | ||
|
|
5a30baad99 | ||
|
|
5050c3d974 | ||
|
|
d8b36606e2 | ||
|
|
37f8fe47ac | ||
|
|
af0d1e4ead | ||
|
|
699f45e486 | ||
|
|
416f04d6bd | ||
|
|
5123e80bd9 | ||
|
|
283a0ab18a | ||
|
|
bab668033e | ||
|
|
1a50dad132 | ||
|
|
d0f9c3d5a4 | ||
|
|
2808988171 | ||
|
|
bd7f393db9 | ||
|
|
d2d6696c05 | ||
|
|
7546aa91a3 | ||
|
|
74d67c39a0 | ||
|
|
20c20f2749 | ||
|
|
2c3020c7fc | ||
|
|
100d348f5e | ||
|
|
b498d5744c | ||
|
|
80eef35e1d | ||
|
|
49a3f1e104 | ||
|
|
0beaa18452 | ||
|
|
76fd461741 | ||
|
|
78697ee62a | ||
|
|
43c5bc17fb | ||
|
|
cda20e0386 | ||
|
|
8f5099e822 | ||
|
|
5dd709ed58 | ||
|
|
9d353bf271 | ||
|
|
633702860f | ||
|
|
bf5b3af285 | ||
|
|
caeaa74704 | ||
|
|
7bf0939a49 | ||
|
|
ec04358796 | ||
|
|
49a28b357c | ||
|
|
dd9e58ddaa | ||
|
|
c723e19b6d | ||
|
|
bb6f9b1f3d | ||
|
|
7bb219e4dd | ||
|
|
9ec31dd543 | ||
|
|
70038bea26 | ||
|
|
a445569139 | ||
|
|
5ad3dca38d | ||
|
|
fd3802f2dc | ||
|
|
a8eee9fe3a | ||
|
|
0a7fa14d0f | ||
|
|
4dde6d2a45 | ||
|
|
78661f11c5 | ||
|
|
faf9d3faac | ||
|
|
75d9845754 | ||
|
|
5c8eb21ab3 | ||
|
|
7d76d789b2 | ||
|
|
a71cc84f11 | ||
|
|
cdcfab0240 | ||
|
|
5a20501577 | ||
|
|
34acdd20cf | ||
|
|
f39b9167b8 | ||
|
|
aa2a357fb2 |
@@ -12,9 +12,14 @@ runs:
|
||||
- name: base vars
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "${GITHUB_BASE_REF:-}" ]; then
|
||||
BASE_HASH=$(git merge-base "origin/${GITHUB_BASE_REF}" HEAD 2>/dev/null || git rev-parse HEAD)
|
||||
else
|
||||
BASE_HASH=$(git rev-parse HEAD)
|
||||
fi
|
||||
{
|
||||
echo "GIT_HASH=$(git rev-parse HEAD)";
|
||||
echo "BASE_HASH=$(git merge-base origin/${GITHUB_BASE_REF} HEAD)";
|
||||
echo "BASE_HASH=${BASE_HASH}";
|
||||
echo "HEAD_HASH=HEAD";
|
||||
echo "HEAD_COMMIT_HASH=${GH_COMMIT}";
|
||||
echo "NX_CALCULATION_FLAGS=--all";
|
||||
@@ -55,7 +60,7 @@ runs:
|
||||
} >> $GITHUB_ENV
|
||||
|
||||
- name: RELEASE on master/develop patch branch
|
||||
if: ${{ env.BREAK_ACTION == false && github.event.pull_request.merged }}
|
||||
if: ${{ env.BREAK_ACTION == false && (github.event.pull_request.merged || github.event_name == 'push') }}
|
||||
shell: bash
|
||||
env:
|
||||
REF_NAME: ${{ github.ref_name }}
|
||||
|
||||
@@ -12,7 +12,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
name: Create git tag
|
||||
env:
|
||||
TAG_NAME: ${{ inputs.tagName }}
|
||||
|
||||
@@ -4,7 +4,7 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Restore dist from cache
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: dist
|
||||
key: dist-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
@@ -13,7 +13,7 @@ runs:
|
||||
fail-on-cache-miss: true
|
||||
|
||||
- name: Restore nxcache from cache
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: nxcache
|
||||
key: nxcache-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
@@ -22,7 +22,7 @@ runs:
|
||||
fail-on-cache-miss: true
|
||||
|
||||
- name: Restore node_modules from cache
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: node_modules
|
||||
key: node-modules-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
|
||||
@@ -23,8 +23,8 @@ runs:
|
||||
env:
|
||||
DRY_RUN_FLAG: ${{ inputs.dry-run-flag }}
|
||||
run: |
|
||||
if [[ '$DRY_RUN_FLAG' == 'true' ]]; then
|
||||
echo "dryrun=--dryrun" >> $GITHUB_OUTPUT;
|
||||
if [[ "$DRY_RUN_FLAG" == 'true' ]]; then
|
||||
echo "dryrun=--dry-run" >> $GITHUB_OUTPUT;
|
||||
echo "enabling dryrun"
|
||||
else
|
||||
echo "dryrun=" >> $GITHUB_OUTPUT;
|
||||
|
||||
@@ -8,7 +8,7 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Save nx cache
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: |
|
||||
nxcache
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
name: 'Setup'
|
||||
description: 'Initialize cache, env var load'
|
||||
inputs:
|
||||
enable-node-modules-cache:
|
||||
description: 'enable caching for node modules'
|
||||
required: false
|
||||
type: boolean
|
||||
default: 'true'
|
||||
cache-suffix:
|
||||
description: 'Suffix to make Nx cache key unique per job (e.g. matrix project name)'
|
||||
required: false
|
||||
@@ -24,39 +19,35 @@ outputs:
|
||||
npm-tag:
|
||||
description: 'NPM tag'
|
||||
value: ${{ steps.set-npm-tag.outputs.npm-tag }}
|
||||
node-modules-cache-hit:
|
||||
description: 'Whether node_modules cache was hit'
|
||||
value: ${{ steps.node-modules-cache.outputs.cache-hit }}
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Set consistent machine ID for Nx cache
|
||||
shell: bash
|
||||
run: echo "nx-github-actions" | sudo tee /etc/machine-id > /dev/null
|
||||
- name: install NPM
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache-dependency-path: package-lock.json
|
||||
cache: 'pnpm'
|
||||
- name: get latest tag sha
|
||||
if: ${{ inputs.full-setup == 'true' }}
|
||||
id: tag-sha
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/git-latest-tag@ed7779a4bda83d699c1acaf72922ef21636a3dc2 # v18.4.0
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/git-latest-tag@1d671f8f10336861c89c67be57c6648d98876103 # v18.19.0
|
||||
- name: load "NPM TAG"
|
||||
if: ${{ inputs.full-setup == 'true' }}
|
||||
id: set-npm-tag
|
||||
uses: ./.github/actions/set-npm-tag
|
||||
- name: Cache node modules
|
||||
id: node-modules-cache
|
||||
if: ${{ inputs.enable-node-modules-cache == 'true' }}
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-node-modules-${{ hashFiles('.nvmrc') }}-${{ hashFiles('package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-modules-${{ hashFiles('.nvmrc') }}-
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Security audit
|
||||
shell: bash
|
||||
run: pnpm audit --audit-level=critical
|
||||
- name: Restore nx cache
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: |
|
||||
nxcache
|
||||
|
||||
@@ -5,19 +5,19 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Save dist to cache
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: dist
|
||||
key: dist-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
|
||||
- name: Save nxcache to cache
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: nxcache
|
||||
key: nxcache-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
|
||||
- name: Save node_modules to cache
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: node_modules
|
||||
key: node-modules-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
|
||||
+19
-49
@@ -32,59 +32,29 @@ updates:
|
||||
- dependency-name: "typescript"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
directories:
|
||||
- "/"
|
||||
- "/.github/actions/before-install"
|
||||
- "/.github/actions/create-git-tag"
|
||||
- "/.github/actions/download-node-modules-and-artifacts"
|
||||
- "/.github/actions/enable-dryrun"
|
||||
- "/.github/actions/get-latest-tag-sha"
|
||||
- "/.github/actions/npm-check-bundle"
|
||||
- "/.github/actions/save-nx-cache"
|
||||
- "/.github/actions/set-npm-tag"
|
||||
- "/.github/actions/setup"
|
||||
- "/.github/actions/upload-node-modules-and-artifacts"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
cooldown:
|
||||
default-days: 3
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
ignore:
|
||||
# Managed by gh aw add. Version-locked to the gh-aw compiler; do not bump.
|
||||
- dependency-name: "github/gh-aw-actions"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/.github/actions/before-install"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
cooldown:
|
||||
default-days: 3
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/.github/actions/download-node-modules-and-artifacts"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
cooldown:
|
||||
default-days: 3
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/.github/actions/enable-dryrun"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
cooldown:
|
||||
default-days: 3
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/.github/actions/get-latest-tag-sha"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
cooldown:
|
||||
default-days: 3
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/.github/actions/npm-check-bundle"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
cooldown:
|
||||
default-days: 3
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/.github/actions/set-npm-tag"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
cooldown:
|
||||
default-days: 3
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/.github/actions/setup"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
cooldown:
|
||||
default-days: 3
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/.github/actions/upload-node-modules-and-artifacts"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
cooldown:
|
||||
default-days: 3
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup environment
|
||||
@@ -28,13 +28,10 @@ jobs:
|
||||
with:
|
||||
cache-suffix: build
|
||||
full-setup: 'false'
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.setup-env.outputs.node-modules-cache-hit != 'true' }}
|
||||
run: npm ci
|
||||
- name: Build affected libs
|
||||
env:
|
||||
BASE_REF: ${{ inputs.base_ref }}
|
||||
run: npx nx affected --target=build --base=origin/$BASE_REF --head=HEAD --configuration=production --exclude=stories
|
||||
run: pnpm nx affected --target=build --base=origin/$BASE_REF --head=HEAD --configuration=production --exclude=stories
|
||||
- name: Save nx cache
|
||||
if: ${{ success() }}
|
||||
uses: ./.github/actions/save-nx-cache
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v3.29.5
|
||||
uses: github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a # 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@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v3.29.5
|
||||
uses: github/codeql-action/autobuild@7188fc363630916deb702c7fdcf4e481b751f97a # 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@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v3.29.5
|
||||
uses: github/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97a # v3.29.5
|
||||
|
||||
@@ -8,13 +8,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.registry_package.package_type == 'npm' && github.event.registry_package.name == 'adf-core'
|
||||
steps:
|
||||
- name: Generate app token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
client-id: ${{ vars.GH_APP_ENGINEERING_CONTRIB_CLIENT_ID }}
|
||||
private-key: ${{ secrets.GH_APP_ENGINEERING_CONTRIB_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
repositories: alfresco-apps
|
||||
permission-contents: write
|
||||
- name: Dispatch event to monorepo
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
PACKAGE_NAME: ${{ github.event.registry_package.name }}
|
||||
PACKAGE_VERSION: ${{ github.event.registry_package.package_version.name }}
|
||||
with:
|
||||
github-token: ${{ secrets.PAT_WRITE_PKG }}
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
retries: 3
|
||||
script: |
|
||||
const payload = {
|
||||
|
||||
@@ -5,21 +5,31 @@ on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
secrets:
|
||||
BOT_GITHUB_TOKEN:
|
||||
GH_APP_ENGINEERING_CONTRIB_PRIVATE_KEY:
|
||||
required: true
|
||||
CROWDIN_TRANSLATIONS_TOKEN:
|
||||
required: true
|
||||
HXPS_GIT_COMMIT_SIGNING_PRIVATE_KEY:
|
||||
required: true
|
||||
jobs:
|
||||
pull-from-crowdin:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Generate app token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
client-id: ${{ vars.GH_APP_ENGINEERING_CONTRIB_CLIENT_ID }}
|
||||
private-key: ${{ secrets.GH_APP_ENGINEERING_CONTRIB_PRIVATE_KEY }}
|
||||
permission-contents: write
|
||||
permission-pull-requests: write
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: develop
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
- name: Pull translations from Crowdin
|
||||
uses: crowdin/github-action@8868a33591d21088edfc398968173a3b98d51706 # v2.16.2
|
||||
uses: crowdin/github-action@e0c8f73cdc0fafde9396e056c5038217000a32d1 # v2.16.4
|
||||
with:
|
||||
skip_ref_checkout: true
|
||||
upload_sources: false
|
||||
@@ -28,6 +38,9 @@ jobs:
|
||||
localization_branch_name: automated-translations-update
|
||||
pull_request_title: "GH auto: Automated Update of Translations from Crowdin"
|
||||
pull_request_base_branch_name: develop
|
||||
github_user_name: ${{ vars.HXPS_GIT_USERNAME }}
|
||||
github_user_email: ${{ vars.HXPS_GIT_EMAIL }}
|
||||
gpg_private_key: ${{ secrets.HXPS_GIT_COMMIT_SIGNING_PRIVATE_KEY }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
CROWDIN_TOKEN: ${{ secrets.CROWDIN_TRANSLATIONS_TOKEN }}
|
||||
|
||||
@@ -41,17 +41,23 @@ jobs:
|
||||
code-changed: ${{ steps.path-filter.outputs.code-changed }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Ensure SHA pinned actions
|
||||
uses: hyland/github-actions-ensure-sha-pinned-actions@7d494b6b53e71f75194d74b4dd890a2266d060b5 # v2.0.0
|
||||
uses: hyland/github-actions-ensure-sha-pinned-actions@7957efb76aba0eec7580a9c0392d3e5bec381359 # v2.0.1
|
||||
|
||||
- name: Check package-lock.json version
|
||||
- name: Check pnpm-lock.yaml version
|
||||
run: |
|
||||
if [[ $(jq '.lockfileVersion == 3' package-lock.json) == "true" ]] ; then
|
||||
echo "package-lock.json has a correct version"
|
||||
if [[ -f "pnpm-lock.yaml" ]]; then
|
||||
LOCKFILE_VERSION=$(grep "^lockfileVersion:" pnpm-lock.yaml | cut -d"'" -f2)
|
||||
if [[ "$LOCKFILE_VERSION" == "9.0" ]]; then
|
||||
echo "pnpm-lock.yaml has correct version: $LOCKFILE_VERSION"
|
||||
else
|
||||
echo "pnpm-lock.yaml must be version 9.0, found: $LOCKFILE_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "package-lock must be version 3"
|
||||
echo "pnpm-lock.yaml is missing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -88,7 +94,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 1
|
||||
@@ -166,7 +172,7 @@ jobs:
|
||||
if: ${{ needs.pre-checks.outputs.code-changed == 'true' }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup environment
|
||||
@@ -174,13 +180,10 @@ jobs:
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
cache-suffix: setup
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.setup-env.outputs.node-modules-cache-hit != 'true' }}
|
||||
run: npm ci
|
||||
- name: Bundle
|
||||
run: |
|
||||
npm run bundle:js-api
|
||||
npm run bundle:cli
|
||||
pnpm bundle:js-api
|
||||
pnpm bundle:cli
|
||||
- name: Save nx cache
|
||||
if: ${{ success() }}
|
||||
uses: ./.github/actions/save-nx-cache
|
||||
@@ -194,7 +197,7 @@ jobs:
|
||||
needs: [setup]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup environment
|
||||
@@ -203,13 +206,10 @@ jobs:
|
||||
with:
|
||||
cache-suffix: lint
|
||||
full-setup: 'false'
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.setup-env.outputs.node-modules-cache-hit != 'true' }}
|
||||
run: npm ci
|
||||
- name: Run lint
|
||||
env:
|
||||
BASE_REF: ${{ github.base_ref || 'develop' }}
|
||||
run: npx nx affected --target=lint --base=origin/$BASE_REF --head=HEAD
|
||||
run: pnpm nx affected --target=lint --base=origin/$BASE_REF --head=HEAD
|
||||
- name: Save nx cache
|
||||
if: ${{ success() }}
|
||||
uses: ./.github/actions/save-nx-cache
|
||||
@@ -230,7 +230,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup environment
|
||||
@@ -239,14 +239,11 @@ jobs:
|
||||
with:
|
||||
cache-suffix: storybook
|
||||
full-setup: 'false'
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.setup-env.outputs.node-modules-cache-hit != 'true' }}
|
||||
run: npm ci
|
||||
- name: Build Storybook
|
||||
env:
|
||||
BASE_REF: ${{ github.base_ref || 'develop' }}
|
||||
run: |
|
||||
npx nx affected --target=build-storybook --base=origin/$BASE_REF --head=HEAD --configuration=ci
|
||||
pnpm nx affected --target=build-storybook --base=origin/$BASE_REF --head=HEAD --configuration=ci
|
||||
- name: Save nx cache
|
||||
if: ${{ success() }}
|
||||
uses: ./.github/actions/save-nx-cache
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Automatic Rebase
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
name: "release"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
dry-run-flag:
|
||||
description: 'enable dry-run on artifact push'
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry-run-flag:
|
||||
@@ -15,13 +8,10 @@ on:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
pull_request:
|
||||
types: [closed]
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- develop-patch*
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- master-patch-*
|
||||
|
||||
@@ -34,8 +24,6 @@ concurrency:
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
BASE_REF: ${{ github.base_ref }}
|
||||
HEAD_REF: ${{ github.head_ref }}
|
||||
GH_COMMIT: ${{ github.sha }}
|
||||
GH_BUILD_NUMBER: ${{ github.run_id }}
|
||||
LOG_LEVEL: "ERROR"
|
||||
@@ -44,22 +32,23 @@ env:
|
||||
jobs:
|
||||
setup:
|
||||
timeout-minutes: 20
|
||||
if: github.event.pull_request.merged == true || github.ref_name == 'master' || github.ref_name == 'master-patch-*' || github.event_name == 'workflow_dispatch'
|
||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||
name: "Setup"
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/setup
|
||||
with:
|
||||
enable-node-modules-cache: false
|
||||
- name: install
|
||||
run: |
|
||||
npm ci
|
||||
npm run bundle:js-api
|
||||
npm run bundle:cli
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm bundle:js-api
|
||||
pnpm bundle:cli
|
||||
- uses: ./.github/actions/upload-node-modules-and-artifacts
|
||||
|
||||
release-npm:
|
||||
@@ -67,21 +56,20 @@ jobs:
|
||||
outputs:
|
||||
release_version: ${{ steps.set-version.outputs.release_version }}
|
||||
timeout-minutes: 30
|
||||
if: github.event.pull_request.merged == true || github.ref_name == 'master' || github.ref_name == 'master-patch-*' || github.event_name == 'workflow_dispatch'
|
||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: read
|
||||
packages: write
|
||||
actions: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- id: setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
enable-node-modules-cache: false
|
||||
- id: set-dryrun
|
||||
uses: ./.github/actions/enable-dryrun
|
||||
with:
|
||||
@@ -100,24 +88,24 @@ jobs:
|
||||
setMigrations();
|
||||
- name: build libraries
|
||||
run: |
|
||||
npm run build:libs
|
||||
npm run build:schematics
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
pnpm build:libs
|
||||
pnpm build:schematics
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
name: release libraries GH registry
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
registry-url: 'https://npm.pkg.github.com'
|
||||
scope: '@alfresco'
|
||||
- run: npm run publish -- --tag=${{ steps.setup.outputs.npm-tag }}
|
||||
- run: pnpm run publish --tag ${{ steps.setup.outputs.npm-tag }} --provenance=false ${{ steps.set-dryrun.outputs.dryrun }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
name: release libraries Npm registry
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
registry-url: 'https://${{ vars.NPM_REGISTRY_ADDRESS }}'
|
||||
scope: '@alfresco'
|
||||
- run: npm run publish -- --tag=${{ steps.setup.outputs.npm-tag }}
|
||||
- run: pnpm run publish --tag ${{ steps.setup.outputs.npm-tag }} ${{ steps.set-dryrun.outputs.dryrun }}
|
||||
|
||||
create-git-tag:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -128,7 +116,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: './.github/actions/create-git-tag'
|
||||
@@ -138,11 +126,11 @@ jobs:
|
||||
npm-check-bundle:
|
||||
needs: [release-npm]
|
||||
timeout-minutes: 15
|
||||
if: github.event.pull_request.merged == true || github.ref_name == 'master' || github.ref_name == 'master-patch-*' || github.event_name == 'workflow_dispatch'
|
||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: ./.github/actions/npm-check-bundle
|
||||
|
||||
push-translation-keys-to-crowdin:
|
||||
@@ -156,10 +144,10 @@ jobs:
|
||||
actions: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Push Source Files to Crowdin
|
||||
uses: crowdin/github-action@8868a33591d21088edfc398968173a3b98d51706 # v2.16.2
|
||||
uses: crowdin/github-action@e0c8f73cdc0fafde9396e056c5038217000a32d1 # v2.16.4
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_sources_args: --delete-obsolete
|
||||
@@ -171,8 +159,9 @@ jobs:
|
||||
name: Run Crowdin pull pipeline for up-to-date sync
|
||||
uses: ./.github/workflows/pull-from-crowdin.yml
|
||||
secrets:
|
||||
BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
GH_APP_ENGINEERING_CONTRIB_PRIVATE_KEY: ${{ secrets.GH_APP_ENGINEERING_CONTRIB_PRIVATE_KEY }}
|
||||
CROWDIN_TRANSLATIONS_TOKEN: ${{ secrets.CROWDIN_TRANSLATIONS_TOKEN }}
|
||||
HXPS_GIT_COMMIT_SIGNING_PRIVATE_KEY: ${{ secrets.HXPS_GIT_COMMIT_SIGNING_PRIVATE_KEY }}
|
||||
|
||||
finalize:
|
||||
if: always()
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
name: Stale PR Cleanup
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
stale-pr-cleanup:
|
||||
uses: Alfresco/alfresco-build-tools/.github/workflows/stale-pr-cleanup.yml@1d671f8f10336861c89c67be57c6648d98876103 # v18.19.0
|
||||
@@ -5,9 +5,8 @@ on:
|
||||
types: [opened, reopened, synchronize]
|
||||
paths:
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
- "pnpm-lock.yaml"
|
||||
- "**/package.json"
|
||||
- "**/package-lock.json"
|
||||
- "pom.xml"
|
||||
- "**/pom.xml"
|
||||
|
||||
@@ -17,7 +16,7 @@ jobs:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/github-upsert-comment@a6a8be7429080c132815d0482a55ae4fd28f15ba # v18.7.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/github-upsert-comment@1d671f8f10336861c89c67be57c6648d98876103 # v18.19.0
|
||||
with:
|
||||
comment-identifier: supply-chain-review-instructions
|
||||
comment-body: |
|
||||
|
||||
+396
-180
File diff suppressed because one or more lines are too long
@@ -10,7 +10,7 @@ permissions:
|
||||
|
||||
engine:
|
||||
id: copilot
|
||||
model: gpt-5.4-nano
|
||||
model: gpt-5-mini
|
||||
|
||||
tools:
|
||||
github:
|
||||
@@ -30,9 +30,11 @@ safe-outputs:
|
||||
hide-older-comments: true
|
||||
add-labels:
|
||||
allowed: [security:low, security:medium, security:high]
|
||||
remove-labels:
|
||||
allowed: [security:low, security:medium, security:high]
|
||||
submit-pull-request-review:
|
||||
|
||||
source: Alfresco/alfresco-build-tools/.github/workflows/supply-chain-review.md@ccf3ddbc10205c171ca84ad88d9a05b1c60069b2
|
||||
source: Alfresco/alfresco-build-tools/.github/workflows/supply-chain-review.md@7479d99410acb8b63f78a5b17abb3b8ebec3a66d
|
||||
---
|
||||
|
||||
# Supply Chain Review
|
||||
@@ -59,13 +61,13 @@ Before collecting external data, identify and exclude internal/private dependenc
|
||||
|
||||
**Internal dependency namespaces (skip these):**
|
||||
|
||||
- **Maven**: Any dependency with a `groupId` starting with `com.hyland.` or `org.alfresco.`
|
||||
- **npm**: Any package under the `@hyland/` or `@alfresco/` scopes
|
||||
- **Maven**: Any dependency with a `groupId` starting with `com.hyland.`, `org.alfresco.`, or `org.activiti.`
|
||||
- **npm**: Any package under the `@hyland/`, `@hylandsoftware/`, or `@alfresco/` scopes
|
||||
|
||||
For each internal dependency found:
|
||||
|
||||
1. Remove it from the analysis pipeline — do NOT query OSV.dev, OpenSSF Scorecard, or registry APIs for these packages (they will fail or return irrelevant data).
|
||||
2. Record the package name, ecosystem, old version, and new version in a separate "Internal Dependencies (Skipped)" list.
|
||||
2. Record the package name (with `@` replaced by `(at)` for GitHub comment compatibility), ecosystem, old version, and new version in a separate "Internal Dependencies (Skipped)" list.
|
||||
3. Continue with Step 2 only for the remaining external/public dependencies.
|
||||
|
||||
If ALL changed dependencies are internal, skip Steps 2-4 and proceed directly to Step 5, posting a report that lists the internal dependencies and notes that no external supply chain analysis was performed.
|
||||
@@ -276,7 +278,18 @@ If no suspicious patterns are found, assign a score of 0-10 and risk level LOW.
|
||||
|
||||
## Step 5 — Post Findings as PR Comment
|
||||
|
||||
Post a structured report in the following format:
|
||||
Post a structured report in the following format.
|
||||
|
||||
**CRITICAL: Sanitize all `@` symbols before posting**
|
||||
|
||||
GitHub enforces a maximum of 10 mentions per comment. Package names containing `@` (like `@hyland/core`, `@alfresco/js-api`) are interpreted as user/team mentions and trigger this limit, causing comment post failures.
|
||||
|
||||
**Before generating the comment text**:
|
||||
|
||||
1. Replace **every** `@` symbol in package names with `(at)` — e.g., `@hyland/core` → `(at)hyland/core`
|
||||
2. Apply this transformation to ALL occurrences: table cells, headings, inline code blocks, findings sections, reason columns
|
||||
3. This applies to both external and internal dependencies
|
||||
4. Do NOT skip this step — even if only a few packages are affected, GitHub counts all `@` symbols
|
||||
|
||||
```txt
|
||||
## Supply Chain Security Review
|
||||
@@ -287,15 +300,15 @@ Post a structured report in the following format:
|
||||
|
||||
### Internal Dependencies (Skipped)
|
||||
|
||||
| Package | Ecosystem | Old Version | New Version | Reason |
|
||||
|--------------|-----------|-------------|-------------|----------------------------|
|
||||
| @hyland/core | npm | 3.1.0 | 3.2.0 | Internal (@hyland/* scope) |
|
||||
| Package | Ecosystem | Old Version | New Version | Reason |
|
||||
|-------------------|-----------|-------------|-------------|---------------------------------|
|
||||
| (at)hyland/core | npm | 3.1.0 | 3.2.0 | Internal ((at)hyland/* scope) |
|
||||
|
||||
_These dependencies are internal packages not available on public registries. External API checks were skipped._
|
||||
|
||||
### Findings
|
||||
|
||||
#### `<package-name>` (<old-version> -> <new-version>) — <RISK_LEVEL>
|
||||
#### `<package-name-sanitized>` (<old-version> -> <new-version>) — <RISK_LEVEL>
|
||||
|
||||
**Risk Score**: <score>/100
|
||||
|
||||
@@ -325,7 +338,8 @@ No suspicious patterns detected. Routine upgrade.
|
||||
|
||||
## Step 6 — Apply Label and Review Status
|
||||
|
||||
- Apply a label to the PR based on the highest risk level found:
|
||||
- First, remove any `security:low`, `security:medium`, or `security:high` labels already present on the PR from a previous review — this PR may have been reviewed before (e.g., after a new commit), and stale risk labels must not remain alongside the new one.
|
||||
- Then apply a label to the PR based on the highest risk level found:
|
||||
- `security:low` for LOW risk
|
||||
- `security:medium` for MEDIUM risk
|
||||
- `security:high` for HIGH or CRITICAL risk
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
hasProjects: ${{ steps.set-matrix.outputs.hasProjects }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup environment
|
||||
@@ -26,16 +26,13 @@ jobs:
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
cache-suffix: test-matrix
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.setup-env.outputs.node-modules-cache-hit != 'true' }}
|
||||
run: npm ci
|
||||
- name: Generate affected projects matrix
|
||||
id: set-matrix
|
||||
env:
|
||||
BASE_REF: ${{ inputs.base_ref }}
|
||||
run: |
|
||||
echo "Base ref is $BASE_REF"
|
||||
AFFECTED_UNIT=$(npx nx show projects --affected --target=test --base=origin/$BASE_REF --head=HEAD --select=projects --plain --exclude=cli,stories,eslint-angular)
|
||||
AFFECTED_UNIT=$(pnpm nx show projects --affected --target=test --base=origin/$BASE_REF --head=HEAD --select=projects --plain --exclude=cli,stories,eslint-angular)
|
||||
echo "Affected projects for UNIT: $AFFECTED_UNIT"
|
||||
|
||||
if [ -z "$AFFECTED_UNIT" ]; then
|
||||
@@ -65,21 +62,18 @@ jobs:
|
||||
include: ${{ fromJson(needs.generate-affected-matrix.outputs.unitMatrix) }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- name: Setup environment
|
||||
id: setup-env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
cache-suffix: test-${{ matrix.project }}
|
||||
full-setup: 'false'
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.setup-env.outputs.node-modules-cache-hit != 'true' }}
|
||||
run: npm ci
|
||||
- name: Run unit tests for ${{ matrix.project }}
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=5120"
|
||||
run: |
|
||||
xvfb-run --auto-servernum npx nx run ${{ matrix.project }}:test
|
||||
xvfb-run --auto-servernum pnpm nx run ${{ matrix.project }}:test
|
||||
- name: Save nx cache
|
||||
if: ${{ success() }}
|
||||
uses: ./.github/actions/save-nx-cache
|
||||
|
||||
+1
-1
@@ -8,7 +8,6 @@ bundles
|
||||
dist
|
||||
tmp
|
||||
temp
|
||||
*.npmrc
|
||||
.history
|
||||
.ng_pkg_build/
|
||||
coverage/
|
||||
@@ -24,3 +23,4 @@ nxcache
|
||||
.husky
|
||||
.cursor/rules/nx-rules.mdc
|
||||
.github/instructions/nx.instructions.md
|
||||
lib/eslint-angular/dist/
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
export NODE_OPTIONS=--max_old_space_size=8192
|
||||
|
||||
lint-staged
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Fail on high severity vulnerabilities during audit
|
||||
audit-level=high
|
||||
|
||||
# Use exact versions to prevent unexpected updates
|
||||
save-exact=true
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"meterian.meterian-heidi"
|
||||
]
|
||||
}
|
||||
@@ -21,6 +21,29 @@ for full details on what you may need to install before using ADF.
|
||||
- [Node Version Manager](docs/tutorials/nvm.md)
|
||||
- [CORS guide](ALFRESCOCORS.md)
|
||||
|
||||
## Installation
|
||||
|
||||
This project uses **pnpm** for package management with built-in supply chain attack protection.
|
||||
|
||||
```bash
|
||||
pnpm install # install all packages
|
||||
pnpm run add <package> # add a new package (with security check)
|
||||
```
|
||||
|
||||
### Supply Chain Security
|
||||
|
||||
**Layer 1: pnpm script blocking**
|
||||
- All lifecycle scripts (postinstall, etc.) are blocked by default
|
||||
- Only trusted packages in `pnpm-workspace.yaml` can run scripts
|
||||
- Protects during `pnpm install` and `pnpm add`
|
||||
|
||||
**Layer 2: Security database check**
|
||||
- `pnpm run add` checks packages against OSV and GitHub Advisory databases BEFORE installing
|
||||
- Pre-commit hook blocks commits containing known malicious packages
|
||||
|
||||
**Layer 3: npm blocked**
|
||||
- Running `npm install` will fail - enforces pnpm usage
|
||||
|
||||
## Components
|
||||
|
||||
You can find the sources for all ADF components in the [`lib`](/lib) folder.
|
||||
|
||||
+9
-2
@@ -351,7 +351,6 @@ for more information about installing and using the source code.
|
||||
|
||||
| Name | Description | Source link |
|
||||
| ---- | ----------- | ----------- |
|
||||
| [Agent Service](content-services/services/agent.service.md) | Manages agents in Content Services. | [Source](../lib/content-services/src/lib/agent/services/agent.service.ts) |
|
||||
| [Audit Service](content-services/services/audit.service.md) | Manages Audit apps and entries. | [Source](../lib/content-services/src/lib/audit/audit.service.ts) |
|
||||
| [Card View Content Update Service](content-services/services/card-view-content-update.service.md) | Manages Card View properties in the content services environment. Implements BaseCardViewContentUpdate. | [Source](../lib/content-services/src/lib/common/services/card-view-content-update.service.ts) | |
|
||||
| [Category tree datasource service](content-services/services/category-tree-datasource.service.md) | Datasource service for category tree. | [Source](../lib/content-services/src/lib/category/services/category-tree-datasource.service.ts) |
|
||||
@@ -367,7 +366,6 @@ for more information about installing and using the source code.
|
||||
| [Node Comments Service](content-services/services/node-comments.service.md) | Adds and retrieves comments for nodes in Content Services. | [Source](../lib/content-services/src/lib/node-comments/services/node-comments.service.ts) |
|
||||
| [Node permission dialog service](content-services/services/node-permission-dialog.service.md) | Displays dialogs to let the user set node permissions. | [Source](../lib/content-services/src/lib/permission-manager/services/node-permission-dialog.service.ts) |
|
||||
| [Node Permission service](content-services/services/node-permission.service.md) | Manages role permissions for content nodes. | [Source](../lib/content-services/src/lib/permission-manager/services/node-permission.service.ts) |
|
||||
| [Search Ai Service](content-services/services/search-ai.service.md) | Manages search AI in Content Services. | [Source](../lib/content-services/src/lib/search-ai/services/search-ai.service.ts) |
|
||||
| [Search filter service](content-services/services/search-filter.service.md) | Registers widgets for use with the Search Filter component. | [Source](../lib/content-services/src/lib/search/services/search-filter.service.ts) |
|
||||
| [Search Query Builder service](content-services/services/search-query-builder.service.md) | Stores information from all the custom search and faceted search widgets, compiles and runs the final search query. | [Source](../lib/content-services/src/lib/search/services/search-query-builder.service.ts) |
|
||||
| [Security Controls service](content-services/services/security-controls.service.md) | Manages security groups & marks in Content Services. | [Source](../lib/content-services/src/lib/security/services/security-controls-groups-marks-security.service.ts) |
|
||||
@@ -567,3 +565,12 @@ Contains all custom rules used by ESLint.
|
||||
<!--eslint-angular end-->
|
||||
|
||||
[(Back to Contents)](#contents)
|
||||
|
||||
## Notes
|
||||
|
||||
### Knowledge Discovery removal (v9.0.0)
|
||||
|
||||
The Knowledge Discovery components, services, and APIs have been removed as of version 9.0.0.
|
||||
The integrated Knowledge Discovery features have been replaced with a section in the side navigation panel containing a link to the standalone Knowledge Discovery UI application in higher level apps such as ACA and ADW.
|
||||
|
||||
If you were using those features, please consider cleaning Local Storage in your browser as *aiReferences* entry may remain.
|
||||
|
||||
@@ -46,9 +46,9 @@ Implements a [search widget](../../../lib/content-services/src/lib/search/models
|
||||
| field | string | Field to apply the query fragment to. Required value |
|
||||
| pattern | string | Regular expression pattern to restrict the format of the input text |
|
||||
| placeholder | string | Text displayed in the widget when the input string is empty |
|
||||
| searchSuffix | string | Text to append always in the search of a string |
|
||||
| searchPrefix | string | Text to prepend always in the search of a string |
|
||||
| allowUpdateOnChange | `boolean` | Enable/Disable the update fire event when text has been changed. By default is true. |
|
||||
| searchSuffix | string | Text to append in the search of a string. Only applied when wildcard matching is enabled (the `search-wildcards-enabled` app config flag, default `true`). |
|
||||
| searchPrefix | string | Text to prepend in the search of a string. Only applied when wildcard matching is enabled (the `search-wildcards-enabled` app config flag, default `true`). |
|
||||
| allowUpdateOnChange | `boolean` | Enable/Disable firing the search update when the text changes. Defaults to `false`; when disabled the search runs only when the user submits the value. |
|
||||
| hideDefaultAction | boolean | Show/hide the widget actions. By default is false. |
|
||||
|
||||
## Details
|
||||
|
||||
@@ -115,8 +115,8 @@ that will be used when performing the actual query.
|
||||
Every query fragment is stored and retrieved using its widget `id`.
|
||||
It is your responsibility to format the query correctly.
|
||||
|
||||
Once your change to the query is finished, update the context and call the `update` method
|
||||
to inform other components about the change:
|
||||
Once your change to the query is finished, update the context and call the `execute` method
|
||||
to rebuild and run the query so the results reflect your change:
|
||||
|
||||
```ts
|
||||
@Component({...})
|
||||
@@ -126,12 +126,15 @@ export class MyComponent implements SearchWidget, OnInit {
|
||||
|
||||
onUIChanged() {
|
||||
this.context.queryFragments[this.id] = `some query`;
|
||||
this.context.update();
|
||||
void this.context.execute();
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** Earlier versions called `this.context.update()` here. The `update()` method and the
|
||||
> `updated` stream have been removed; call `this.context.execute()` directly instead.
|
||||
|
||||
When executed, your fragment will be injected into the resulting query based on the category order in the application configuration file.
|
||||
|
||||
```text
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
Title: Agent service
|
||||
Added: v7.0.0-alpha.3
|
||||
Status: Active
|
||||
Last reviewed: 2024-07-12
|
||||
---
|
||||
|
||||
# [Agent service](../../../lib/content-services/src/lib/agent/services/agent.service.ts "Defined in agent.service.ts")
|
||||
|
||||
Manages agents in Content Services.<br/>
|
||||
|
||||
<b>In order to use this service, you need to have the HX Insights Connector (additional ACS module) installed.</b>
|
||||
|
||||
## Class members
|
||||
|
||||
### Methods
|
||||
|
||||
- **getAgents**(): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`AgentPaging`](../../../lib/js-api/src/api/content-rest-api/docs/AgentsApi.md#agentpaging)`>`<br/>
|
||||
Gets all agents.
|
||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`AgentPaging`](../../../lib/js-api/src/api/content-rest-api/docs/AgentsApi.md#agentpaging)`>` - AgentPaging object containing the agents.
|
||||
|
||||
## Details
|
||||
|
||||
See the
|
||||
[Agents API](../../../lib/js-api/src/api/content-rest-api/docs/AgentsApi.md) for more information about the types returned by [Agent
|
||||
service](agent.service.md) methods and for the implementation of the REST API the service is
|
||||
based on.
|
||||
@@ -23,11 +23,12 @@ Manages Document List information that is specific to a user.
|
||||
- _node:_ `any` - Node object
|
||||
- _nodeId:_ `string` - ID of the node object
|
||||
- **Returns** `string` - ID value
|
||||
- **getRecentFiles**(personId: `string`, pagination: [`PaginationModel`](../../../lib/core/src/lib/models/pagination.model.ts), filters?: `string[]`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodePaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/NodePaging.md)`>`<br/>
|
||||
- **getRecentFiles**(personId: `string`, pagination: [`PaginationModel`](../../../lib/core/src/lib/models/pagination.model.ts), filters?: `string[]`, includeFields: `string[]` = `[]`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodePaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/NodePaging.md)`>`<br/>
|
||||
Gets files recently accessed by a user.
|
||||
- _personId:_ `string` - ID of the user
|
||||
- _pagination:_ [`PaginationModel`](../../../lib/core/src/lib/models/pagination.model.ts) - Specifies how to paginate the results
|
||||
- _filters:_ `string[]` - (Optional) Specifies additional filters to apply (joined with **AND**)
|
||||
- _includeFields:_ `string[]` - List of data field names to include in the results
|
||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`NodePaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/NodePaging.md)`>` - List of nodes for the recently used files
|
||||
- **hasCorrespondingNodeIds**(nodeId: `string`): `boolean`<br/>
|
||||
Does the well-known alias have a corresponding node ID?
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Title: Document List service
|
||||
Added: v2.0.0
|
||||
Status: Active
|
||||
Last reviewed: 2019-01-16
|
||||
Last reviewed: 2026-06-16
|
||||
---
|
||||
|
||||
# Document List Service
|
||||
@@ -21,10 +21,11 @@ import { DocumentListService } from '@alfresco/adf-core';
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Description |
|
||||
|-------------------|-------------------------------------------------|
|
||||
| `reload$` | Emits when the document list should be reloaded |
|
||||
| `resetSelection$` | Emits when the selection should be reset |
|
||||
| Name | Description |
|
||||
|-------------------|----------------------------------------------------------------------------------------------|
|
||||
| `reload$` | Emits when the document list should be reloaded. |
|
||||
| `resetSelection$` | Emits when the selection should be reset. |
|
||||
| `reloadSilently$` | Emits when the document list should be reloaded **without** resetting the current selection. |
|
||||
|
||||
### Methods
|
||||
|
||||
@@ -32,6 +33,8 @@ import { DocumentListService } from '@alfresco/adf-core';
|
||||
Reloads the document list.
|
||||
- **resetSelection**(): `void`<br/>
|
||||
Resets the selection.
|
||||
- **reloadSilently**(): `void`<br/>
|
||||
Reloads the document list **without** resetting the current selection.
|
||||
- **copyNode**(nodeId: `string`, targetParentId: `string`): `Observable<NodeEntry>`<br/>
|
||||
Copy a node to destination node
|
||||
- _nodeId:_ `string` - The id of the node to be copied
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
Title: Search Ai service
|
||||
Added: v7.0.0-alpha.3
|
||||
Status: Active
|
||||
Last reviewed: 2024-07-12
|
||||
---
|
||||
|
||||
# [Search Ai service](../../../lib/content-services/src/lib/search-ai/services/search-ai.service.ts "Defined in search-ai.service.ts")
|
||||
|
||||
Manages search AI in Content Services.
|
||||
|
||||
<b>In order to use this service, you need to have the HX Insights Connector (additional ACS module) installed.</b>
|
||||
|
||||
## Class members
|
||||
|
||||
### Methods
|
||||
|
||||
- **updateSearchAiInputState**(state: `SearchAiInputState`): `void`<br/>
|
||||
Update the state of the search AI input.
|
||||
- _state:_ `SearchAiInputState` - The new state of the search AI input.
|
||||
- **ask**(question: [`QuestionRequest`](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md#questionrequest)): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`QuestionModel`](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md#questionmodel)`>`<br/>
|
||||
Ask a question to the AI.
|
||||
- _question:_ [`QuestionRequest`](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md#questionrequest) - The question to ask.
|
||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`QuestionModel`](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md#questionmodel)`>` - QuestionModel object containing information about questions.
|
||||
- **getAnswer**(questionId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`AiAnswerEntry`](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md#aianswerentry)`>`<br/>
|
||||
Get an answer to specific question.
|
||||
- _questionId:_ `string` - The ID of the question to get an answer for.
|
||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`AiAnswerEntry`](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md#aianswerentry)`>` - AiAnswerEntry object containing the answer.
|
||||
- **getConfig**(): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`KnowledgeRetrievalConfigEntry`](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md#knowledgeretrievalconfigentry)`>`<br/>
|
||||
Get the knowledge retrieval configuration.
|
||||
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`KnowledgeRetrievalConfigEntry`](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md#knowledgeretrievalconfigentry)`>` - KnowledgeRetrievalConfigEntry object containing the configuration.
|
||||
- **checkSearchAvailability**(selectedNodesState: `SelectionState`, maxSelectedNodes: `number`): `string`<br/>
|
||||
Check if using of search is possible (if all conditions are met).
|
||||
- _selectedNodesState:_ `SelectionState` - information about selected nodes.
|
||||
- _maxSelectedNodes:_ `number` - max number of selected nodes. Default 100.
|
||||
- **Returns** `string` - string with error if any condition is not met, empty string otherwise.
|
||||
|
||||
## Details
|
||||
|
||||
See the
|
||||
[Search Ai API](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md) for more information about the types returned by [Search Ai
|
||||
service](search-ai.service.md) methods and for the implementation of the REST API the service is
|
||||
based on.
|
||||
@@ -2,7 +2,7 @@
|
||||
Title: Search Query Builder service
|
||||
Added: v2.3.0
|
||||
Status: Active
|
||||
Last reviewed: 2019-03-19
|
||||
Last reviewed: 2026-06-29
|
||||
---
|
||||
|
||||
# [Search Query Builder service](../../../lib/content-services/src/lib/search/services/search-query-builder.service.ts "Defined in search-query-builder.service.ts")
|
||||
@@ -11,6 +11,17 @@ Stores information from all the custom search and faceted search widgets, compil
|
||||
|
||||
## Class members
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Description |
|
||||
| ----------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| userQuery | `string` | The raw query string typed by the user. Setting it stores the value, records it in `filterRawParams` and recomputes `parsedQuery` according to the current `searchMode`. |
|
||||
| parsedQuery | `string` (read-only) | The query derived from `userQuery`. In `regular` mode the user terms are expanded against the configured fields (see `app:fields`) and optionally wildcarded; in `formula` mode it is identical to `userQuery`. |
|
||||
| searchMode | `'regular' \| 'formula'` | Controls how `userQuery` is turned into `parsedQuery`. `regular` (default) parses the user input into a field query; `formula` uses the user input verbatim as an AFTS expression. |
|
||||
| selectedConfigurationId | `string` | Id of the currently active search configuration. Setting it also records the value in `filterRawParams`. |
|
||||
| encodedQuery | `string` (read-only) | The Base64-encoded `filterRawParams`, produced by `encodeQuery()` and written to the `q` route query parameter. |
|
||||
| wildcardsEnabled | `boolean` (read-only) | Reads the `search-wildcards-enabled` app config flag (default `true`). When enabled, query terms are suffixed with `*` so partial matches are returned. |
|
||||
|
||||
### Methods
|
||||
|
||||
- **addFilterQuery**(query: `string`)<br/>
|
||||
@@ -25,9 +36,10 @@ Stores information from all the custom search and faceted search widgets, compil
|
||||
- **Returns** `SearchRequest` - The finished query
|
||||
- **encodeQuery**()<br/>
|
||||
Encodes query shards stored in `filterRawParams` property.
|
||||
- **execute**(queryBody?: `SearchRequest`)<br/>
|
||||
Builds and executes the current query.
|
||||
- _queryBody:_ `SearchRequest` - (Optional)
|
||||
- **execute**(updateQueryParams: `boolean` = `true`, queryBody?: `SearchRequest`)<br/>
|
||||
Builds and executes the current query, then emits the result on the `executed` stream.
|
||||
- _updateQueryParams:_ `boolean` - (Optional) When `true` (default) the encoded query is written to the `q` route query parameter. Pass `false` to execute without updating the URL.
|
||||
- _queryBody:_ `SearchRequest` - (Optional) Pre-built query to execute instead of building one from the current state.
|
||||
- **getDefaultConfiguration**(): [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)`|undefined`<br/>
|
||||
|
||||
- **Returns** [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)`|undefined` -
|
||||
@@ -70,6 +82,11 @@ Stores information from all the custom search and faceted search widgets, compil
|
||||
|
||||
- **Returns** `boolean` -
|
||||
|
||||
- **isOperator**(input: `string`): `boolean`<br/>
|
||||
Checks whether the supplied string is a logical `AND` or `OR` operator. Used when parsing a multi-word user query in `regular` search mode.
|
||||
- _input:_ `string` - String to check
|
||||
- **Returns** `boolean` - `true` if the trimmed string is `AND` or `OR`, otherwise `false`
|
||||
|
||||
- **loadConfiguration**(): [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts)<br/>
|
||||
|
||||
- **Returns** [`SearchConfiguration`](../../../lib/content-services/src/lib/search/models/search-configuration.interface.ts) -
|
||||
@@ -86,7 +103,10 @@ Stores information from all the custom search and faceted search widgets, compil
|
||||
Removes an existing bucket from a field.
|
||||
- _field:_ [`FacetField`](../../../lib/content-services/src/lib/search/models/facet-field.interface.ts) - The target field
|
||||
- _bucket:_ [`FacetFieldBucket`](../../../lib/content-services/src/lib/search/models/facet-field-bucket.interface.ts) - Bucket to remove
|
||||
- **resetToDefaults**()<br/>
|
||||
- **resetToDefaults**(withNavigate: `boolean` = `false`, resetUserQuery: `boolean` = `true`)<br/>
|
||||
Resets the query builder back to the default search configuration.
|
||||
- _withNavigate:_ `boolean` - (Optional) When `true`, clears the `q` route query parameter while resetting. Defaults to `false`.
|
||||
- _resetUserQuery:_ `boolean` - (Optional) When `true` (default), the `userQuery` and its parsed form are cleared. Pass `false` to keep the current user query while resetting the rest of the options.
|
||||
|
||||
- **search**(queryBody: `SearchRequest`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`ResultSetPaging`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/search-rest-api/docs/ResultSetPaging.md)`>`<br/>
|
||||
|
||||
@@ -97,14 +117,13 @@ Stores information from all the custom search and faceted search widgets, compil
|
||||
|
||||
- _scope:_ `RequestScope` -
|
||||
|
||||
- **update**(queryBody?: `SearchRequest`)<br/>
|
||||
Builds the current query and triggers the `updated` event.
|
||||
- _queryBody:_ `SearchRequest` - (Optional)
|
||||
- **updateSearchQueryParams**() <br/>
|
||||
Encodes the query and navigates to existing search route adding encoded query as a search param.
|
||||
- **updateSelectedConfiguration**(index: `number`)<br/>
|
||||
|
||||
- _index:_ `number` -
|
||||
- **updateSelectedConfiguration**(id: `string`, resetFilters: `boolean` = `true`, shouldExecute: `boolean` = `true`)<br/>
|
||||
Switches the active search configuration to the one matching the supplied id (only relevant when multiple configurations are provided).
|
||||
- _id:_ `string` - Id of the configuration to select
|
||||
- _resetFilters:_ `boolean` - (Optional) When `true` (default), the current search options are reset before applying the new configuration. Pass `false` to keep them.
|
||||
- _shouldExecute:_ `boolean` - (Optional) When `true` (default), the query is executed immediately after switching configuration.
|
||||
|
||||
## Details
|
||||
|
||||
@@ -127,10 +146,6 @@ You can use custom widgets to populate and edit the following parts of the resul
|
||||
```ts
|
||||
constructor(queryBuilder: SearchQueryBuilderService) {
|
||||
|
||||
queryBuilder.updated.subscribe(query => {
|
||||
this.queryBuilder.execute();
|
||||
});
|
||||
|
||||
queryBuilder.executed.subscribe(data => {
|
||||
this.onDataLoaded(data);
|
||||
});
|
||||
@@ -138,6 +153,29 @@ constructor(queryBuilder: SearchQueryBuilderService) {
|
||||
}
|
||||
```
|
||||
|
||||
To run a search, build the query state (for example by setting `userQuery` or by letting a
|
||||
search widget update `queryFragments`) and then call `execute()`. The result is delivered
|
||||
through the `executed` stream.
|
||||
|
||||
```ts
|
||||
this.queryBuilder.userQuery = 'invoice';
|
||||
void this.queryBuilder.execute();
|
||||
```
|
||||
|
||||
> **Note:** Earlier versions exposed an `updated` stream and an `update()` method that built the
|
||||
> query and emitted it so that a subscriber could call `execute()`. Both have been removed; build
|
||||
> the query state and call `execute()` directly instead.
|
||||
|
||||
### Search modes
|
||||
|
||||
The builder supports two search modes, selected through the `searchMode` property:
|
||||
|
||||
- **regular** (default) - the text in `userQuery` is treated as user input and parsed into a
|
||||
field query. The query is split into terms, each term is matched against the fields listed in
|
||||
the `app:fields` search configuration entry (falling back to `cm:name`), and a `*` wildcard is
|
||||
appended when wildcards are enabled. Bare `AND`/`OR` words are preserved as operators.
|
||||
- **formula** - the text in `userQuery` is used verbatim as an [AFTS](https://docs.alfresco.com/content-services/latest/develop/search-api/) expression, allowing callers that already build their own query syntax to bypass parsing.
|
||||
|
||||
> **Note:** From ADF 3.0.0, the query contains the `"facetFormat": "V2"` parameter so that all the responses have the same structure whether they come from search queries containing facetFields, facetQueries, grouped facetQueries or facetIntervals.
|
||||
|
||||
## Runtime Configuration
|
||||
|
||||
@@ -62,3 +62,5 @@ The pages linked below contain the licenses for all third party dependencies of
|
||||
- [ADF 8.2.1](license-info-8.2.1.md)
|
||||
- [ADF 8.3.1](license-info-8.3.1.md)
|
||||
- [ADF 8.4.1](license-info-8.4.1.md)
|
||||
- [ADF 8.5.0](license-info-8.5.0.md)
|
||||
- [ADF 9.0.0](license-info-9.0.0.md)
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
Title: License info, alfresco-ng2-components 8.5.0
|
||||
---
|
||||
|
||||
# License information for alfresco-ng2-components 8.5.0
|
||||
|
||||
This page lists all third party libraries the project depends on.
|
||||
|
||||
## Libraries
|
||||
|
||||
| Name | Version | License |
|
||||
| --- | --- | --- |
|
||||
| [@angular/animations](https://github.com/angular/angular.git) | 19.2.20 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/cdk](https://github.com/angular/components.git) | 19.2.19 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/common](https://github.com/angular/angular.git) | 19.2.20 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/compiler](https://github.com/angular/angular.git) | 19.2.20 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/core](https://github.com/angular/angular.git) | 19.2.20 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/forms](https://github.com/angular/angular.git) | 19.2.20 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/material](https://github.com/angular/components.git) | 19.2.19 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/material-date-fns-adapter](https://github.com/angular/components.git) | 19.2.19 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/platform-browser](https://github.com/angular/angular.git) | 19.2.20 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/platform-browser-dynamic](https://github.com/angular/angular.git) | 19.2.20 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/router](git+https://github.com/angular/angular.git) | 19.2.20 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@apollo/client](git+https://github.com/apollographql/apollo-client.git) | 3.13.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@babel/runtime](https://github.com/babel/babel.git) | 7.26.10 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@cspell/eslint-plugin](https://github.com/streetsidesoftware/cspell.git) | 10.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@graphql-typed-document-node/core](git@github.com:dotansimha/graphql-typed-document-node.git) | 3.2.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@mat-datetimepicker/core](https://github.com/kuhnroyal/mat-datetimepicker.git) | 15.0.2 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@napi-rs/canvas](git+https://github.com/Brooooooklyn/canvas.git) | 0.1.100 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@napi-rs/canvas-darwin-arm64](git+https://github.com/Brooooooklyn/canvas.git) | 0.1.100 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@ngx-translate/core](https://github.com/ngx-translate/core) | 17.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@wry/caches](git+https://github.com/benjamn/wryware.git) | 1.0.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@wry/context](git+https://github.com/benjamn/wryware.git) | 0.7.4 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@wry/equality](git+https://github.com/benjamn/wryware.git) | 0.5.7 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@wry/trie](git+https://github.com/benjamn/wryware.git) | 0.5.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [angular-oauth2-oidc](https://github.com/manfredsteyer/angular-oauth2-oidc) | 19.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [apollo-angular](https://github.com/kamilkisiela/apollo-angular) | 10.0.3 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [balanced-match](git://github.com/juliangruber/balanced-match.git) | 1.0.2 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [brace-expansion](git://github.com/juliangruber/brace-expansion.git) | 1.1.15 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [brace-expansion](git://github.com/juliangruber/brace-expansion.git) | 2.1.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [concat-map](git://github.com/substack/node-concat-map.git) | 0.0.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [cropperjs](git+https://github.com/fengyuanchen/cropperjs.git) | 1.6.2 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [date-fns](https://github.com/date-fns/date-fns) | 2.30.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| dotenv-expand | 5.1.0 | [BSD-2-Clause](https://opensource.org/licenses/BSD-2-Clause) |
|
||||
| editorjs-html | 4.0.5 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [eve-raphael](https://github.com/tomasAlabes/eve.git) | 0.5.0 | UNKNOWN |
|
||||
| [eventemitter3](git://github.com/primus/eventemitter3.git) | 5.0.4 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [fetch-blob](https://github.com/node-fetch/fetch-blob.git) | 3.2.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [formdata-polyfill](git+https://jimmywarting@github.com/jimmywarting/FormData.git) | 4.0.10 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [graphql-tag](git+https://github.com/apollographql/graphql-tag.git) | 2.12.6 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [graphql-ws](git+https://github.com/enisdenjo/graphql-ws.git) | 6.0.8 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [hoist-non-react-statics](git://github.com/mridgway/hoist-non-react-statics.git) | 3.3.2 | [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) |
|
||||
| [js-tokens](lydell/js-tokens) | 4.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [lodash-es](lodash/lodash) | 4.18.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [loose-envify](git://github.com/zertosh/loose-envify.git) | 1.4.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [material-icons](git+https://github.com/marella/material-icons.git) | 1.13.14 | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
|
||||
| [minimatch](git://github.com/isaacs/minimatch.git) | 5.1.9 | [ISC](https://www.isc.org/licenses/) |
|
||||
| [minimatch-browser](git://github.com/isaacs/minimatch.git) | 1.0.0 | [ISC](https://www.isc.org/licenses/) |
|
||||
| [ng2-charts](git+https://github.com/valor-software/ng2-charts.git) | 4.1.1 | [ISC](https://www.isc.org/licenses/) |
|
||||
| [node-domexception](git+https://github.com/jimmywarting/node-domexception.git) | 1.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [node-fetch](https://github.com/node-fetch/node-fetch.git) | 3.3.2 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [object-assign](sindresorhus/object-assign) | 4.1.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [optimism](git+https://github.com/benjamn/optimism.git) | 0.18.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [pdfjs-dist](git+https://github.com/mozilla/pdf.js.git) | 5.1.91 | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
|
||||
| [prop-types](facebook/prop-types) | 15.8.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [raphael](git://github.com/DmitryBaranovskiy/raphael.git) | 2.3.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [react-is](https://github.com/facebook/react.git) | 16.13.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [regenerator-runtime](https://github.com/facebook/regenerator/tree/main/packages/runtime) | 0.14.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [rehackt](git+https://github.com/phryneas/rehackt.git) | 0.1.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [rxjs](https://github.com/reactivex/rxjs.git) | 7.8.2 | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
|
||||
| [symbol-observable](blesh/symbol-observable) | 4.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [ts-invariant](git+https://github.com/apollographql/invariant-packages.git) | 0.10.3 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [tslib](https://github.com/Microsoft/tslib.git) | 2.8.1 | [0BSD](http://landley.net/toybox/license.html) |
|
||||
| [uuid](https://github.com/uuidjs/uuid.git) | 14.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [web-streams-polyfill](git+https://github.com/MattiasBuelens/web-streams-polyfill.git) | 3.3.3 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [zen-observable](zenparsing/zen-observable) | 0.8.15 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [zen-observable-ts](git+https://github.com/apollographql/zen-observable-ts.git) | 1.2.5 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [zone.js](git://github.com/angular/angular.git) | 0.15.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
Title: License info, alfresco-ng2-components 9.0.0
|
||||
---
|
||||
|
||||
# License information for alfresco-ng2-components 9.0.0
|
||||
|
||||
This page lists all third party libraries the project depends on.
|
||||
|
||||
## Libraries
|
||||
|
||||
| Name | Version | License |
|
||||
| --- | --- | --- |
|
||||
| [@angular-eslint/bundled-angular-compiler](https://github.com/angular-eslint/angular-eslint.git) | 20.7.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular-eslint/utils](https://github.com/angular-eslint/angular-eslint.git) | 20.7.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/animations](https://github.com/angular/angular.git) | 20.3.25 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/cdk](https://github.com/angular/components.git) | 20.2.14 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/common](https://github.com/angular/angular.git) | 20.3.25 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/compiler](https://github.com/angular/angular.git) | 20.3.25 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/core](https://github.com/angular/angular.git) | 20.3.25 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/forms](https://github.com/angular/angular.git) | 20.3.25 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/material](https://github.com/angular/components.git) | 20.2.14 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/material-date-fns-adapter](https://github.com/angular/components.git) | 20.2.14 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/platform-browser](https://github.com/angular/angular.git) | 20.3.25 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/platform-browser-dynamic](https://github.com/angular/angular.git) | 20.3.25 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@angular/router](git+https://github.com/angular/angular.git) | 20.3.25 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@apollo/client](git+https://github.com/apollographql/apollo-client.git) | 3.13.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@babel/runtime](https://github.com/babel/babel.git) | 7.26.10 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@cspell/eslint-plugin](https://github.com/streetsidesoftware/cspell.git) | 10.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@graphql-typed-document-node/core](git@github.com:dotansimha/graphql-typed-document-node.git) | 3.2.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@mat-datetimepicker/core](https://github.com/kuhnroyal/mat-datetimepicker.git) | 16.0.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@napi-rs/canvas](git+https://github.com/Brooooooklyn/canvas.git) | 0.1.100 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@napi-rs/canvas-darwin-arm64](git+https://github.com/Brooooooklyn/canvas.git) | 0.1.100 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@ngx-translate/core](https://github.com/ngx-translate/core) | 17.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@wry/caches](git+https://github.com/benjamn/wryware.git) | 1.0.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@wry/context](git+https://github.com/benjamn/wryware.git) | 0.7.4 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@wry/equality](git+https://github.com/benjamn/wryware.git) | 0.5.7 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [@wry/trie](git+https://github.com/benjamn/wryware.git) | 0.5.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [angular-oauth2-oidc](https://github.com/manfredsteyer/angular-oauth2-oidc) | 19.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [apollo-angular](https://github.com/kamilkisiela/apollo-angular) | 11.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [balanced-match](git://github.com/juliangruber/balanced-match.git) | 1.0.2 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [brace-expansion](git://github.com/juliangruber/brace-expansion.git) | 1.1.15 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [brace-expansion](git://github.com/juliangruber/brace-expansion.git) | 2.1.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [concat-map](git://github.com/substack/node-concat-map.git) | 0.0.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [cropperjs](git+https://github.com/fengyuanchen/cropperjs.git) | 1.6.2 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [date-fns](https://github.com/date-fns/date-fns) | 2.30.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| dotenv-expand | 5.1.0 | [BSD-2-Clause](https://opensource.org/licenses/BSD-2-Clause) |
|
||||
| editorjs-html | 4.0.5 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [eve-raphael](https://github.com/tomasAlabes/eve.git) | 0.5.0 | UNKNOWN |
|
||||
| [eventemitter3](git://github.com/primus/eventemitter3.git) | 5.0.4 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [fetch-blob](https://github.com/node-fetch/fetch-blob.git) | 3.2.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [formdata-polyfill](git+https://jimmywarting@github.com/jimmywarting/FormData.git) | 4.0.10 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [graphql-tag](git+https://github.com/apollographql/graphql-tag.git) | 2.12.7 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [graphql-ws](git+https://github.com/enisdenjo/graphql-ws.git) | 6.0.8 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [hoist-non-react-statics](git://github.com/mridgway/hoist-non-react-statics.git) | 3.3.2 | [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) |
|
||||
| [js-tokens](lydell/js-tokens) | 4.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [lodash-es](lodash/lodash) | 4.18.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [loose-envify](git://github.com/zertosh/loose-envify.git) | 1.4.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [material-icons](git+https://github.com/marella/material-icons.git) | 1.13.14 | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
|
||||
| [minimatch](git://github.com/isaacs/minimatch.git) | 5.1.9 | [ISC](https://www.isc.org/licenses/) |
|
||||
| [minimatch-browser](git://github.com/isaacs/minimatch.git) | 1.0.0 | [ISC](https://www.isc.org/licenses/) |
|
||||
| [ng2-charts](git+https://github.com/valor-software/ng2-charts.git) | 4.1.1 | [ISC](https://www.isc.org/licenses/) |
|
||||
| [node-domexception](git+https://github.com/jimmywarting/node-domexception.git) | 1.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [node-fetch](https://github.com/node-fetch/node-fetch.git) | 3.3.2 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [object-assign](sindresorhus/object-assign) | 4.1.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [optimism](git+https://github.com/benjamn/optimism.git) | 0.18.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [pdfjs-dist](git+https://github.com/mozilla/pdf.js.git) | 5.1.91 | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
|
||||
| [prop-types](facebook/prop-types) | 15.8.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [raphael](git://github.com/DmitryBaranovskiy/raphael.git) | 2.3.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [react-is](https://github.com/facebook/react.git) | 16.13.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [regenerator-runtime](https://github.com/facebook/regenerator/tree/main/packages/runtime) | 0.14.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [rehackt](git+https://github.com/phryneas/rehackt.git) | 0.1.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [rxjs](https://github.com/reactivex/rxjs.git) | 7.8.2 | [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) |
|
||||
| [symbol-observable](blesh/symbol-observable) | 4.0.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [ts-invariant](git+https://github.com/apollographql/invariant-packages.git) | 0.10.3 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [tslib](https://github.com/Microsoft/tslib.git) | 2.8.1 | [0BSD](http://landley.net/toybox/license.html) |
|
||||
| [uuid](https://github.com/uuidjs/uuid.git) | 14.0.1 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [web-streams-polyfill](git+https://github.com/MattiasBuelens/web-streams-polyfill.git) | 3.3.3 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [zen-observable](zenparsing/zen-observable) | 0.8.15 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [zen-observable-ts](git+https://github.com/apollographql/zen-observable-ts.git) | 1.2.5 | [MIT](https://opensource.org/license/mit/) |
|
||||
| [zone.js](git://github.com/angular/angular.git) | 0.15.0 | [MIT](https://opensource.org/license/mit/) |
|
||||
|
||||
@@ -9,6 +9,8 @@ The first **General Availability** release was v2.0.0.
|
||||
|
||||
## General Availability
|
||||
|
||||
- [9.0.0](RelNote-9.0.0.md)
|
||||
- [8.5.0](RelNote-8.5.0.md)
|
||||
- [8.4.1](RelNote-8.4.1.md)
|
||||
- [8.3.1](RelNote-8.3.1.md)
|
||||
- [8.2.1](RelNote-8.2.1.md)
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
Title: Changelog for alfresco-ng2-components v8.5.0
|
||||
---
|
||||
|
||||
# Changelog
|
||||
|
||||
- [ec04358796](https://github.com/Alfresco/alfresco-ng2-components/commit/ec04358796) AAE-46943 Rename git signing secret/vars to HXPS_GIT_* (#11978)
|
||||
- [49a28b357c](https://github.com/Alfresco/alfresco-ng2-components/commit/49a28b357c) AAE-46943 Sign Crowdin translation PR commits with GPG (#11974)
|
||||
- [dd9e58ddaa](https://github.com/Alfresco/alfresco-ng2-components/commit/dd9e58ddaa) [ACS-11927] init acs aps scripts update terraform (#11976)
|
||||
- [c723e19b6d](https://github.com/Alfresco/alfresco-ng2-components/commit/c723e19b6d) AAE-46971 Update Supply Chain Review GH AW to latest (#11972)
|
||||
- [bb6f9b1f3d](https://github.com/Alfresco/alfresco-ng2-components/commit/bb6f9b1f3d) AAE-46434 Fix disabled stying for people widgets (#11967)
|
||||
- [7bb219e4dd](https://github.com/Alfresco/alfresco-ng2-components/commit/7bb219e4dd) AAE-46654 Fix - return empty string when no value is provided in card… (#11969)
|
||||
- [70038bea26](https://github.com/Alfresco/alfresco-ng2-components/commit/70038bea26) AAE-46246 Format typed values in form display widgets v2 (#11952)
|
||||
- [5ad3dca38d](https://github.com/Alfresco/alfresco-ng2-components/commit/5ad3dca38d) AAE-46684 fix release workflow eslint publish fix provenance (#11963)
|
||||
- [fd3802f2dc](https://github.com/Alfresco/alfresco-ng2-components/commit/fd3802f2dc) AAE-46684 fix release workflow eslint publish fix (#11962)
|
||||
- [a8eee9fe3a](https://github.com/Alfresco/alfresco-ng2-components/commit/a8eee9fe3a) Improvements/aae 46684 fix release workflow fixed command (#11961)
|
||||
- [0a7fa14d0f](https://github.com/Alfresco/alfresco-ng2-components/commit/0a7fa14d0f) Improvements/aae 46684 fix release workflow ignore (#11960)
|
||||
- [4dde6d2a45](https://github.com/Alfresco/alfresco-ng2-components/commit/4dde6d2a45) [AAE-46514] - Fix release workflow (#11959)
|
||||
- [78661f11c5](https://github.com/Alfresco/alfresco-ng2-components/commit/78661f11c5) [AAE-46514] - Migration to PNPM (#11926)
|
||||
- [faf9d3faac](https://github.com/Alfresco/alfresco-ng2-components/commit/faf9d3faac) AAE-46388 Update Supply Chain Review GH AW to latest (#11957)
|
||||
- [5c8eb21ab3](https://github.com/Alfresco/alfresco-ng2-components/commit/5c8eb21ab3) AAE-46387 Standardize DependaBot config schedule/grouping (#11953)
|
||||
- [7d76d789b2](https://github.com/Alfresco/alfresco-ng2-components/commit/7d76d789b2) AAE-41418 Add process instance id input to task header cloud component (#11897)
|
||||
- [a71cc84f11](https://github.com/Alfresco/alfresco-ng2-components/commit/a71cc84f11) AAE-46507 Next Task Endpoint (#11943)
|
||||
- [34acdd20cf](https://github.com/Alfresco/alfresco-ng2-components/commit/34acdd20cf) Revert "AAE-46246 Format typed values in form display widgets (#11935)" (#11951)
|
||||
- [f39b9167b8](https://github.com/Alfresco/alfresco-ng2-components/commit/f39b9167b8) AAE-46246 Format typed values in form display widgets (#11935)
|
||||
- [aa2a357fb2](https://github.com/Alfresco/alfresco-ng2-components/commit/aa2a357fb2) AAE-46434 Fix read-only field styling on forms (#11928)
|
||||
- [f9899883dc](https://github.com/Alfresco/alfresco-ng2-components/commit/f9899883dc) Revert AAE-46242 crowdin locale mapping changes (#11949)
|
||||
- [46b2d0380b](https://github.com/Alfresco/alfresco-ng2-components/commit/46b2d0380b) AAE-45774 Shorten invalid date error message (#11920)
|
||||
- [274ddb3098](https://github.com/Alfresco/alfresco-ng2-components/commit/274ddb3098) AAE-46386 Set DependaBot cooldown for all ecosystems as 3d (#11947)
|
||||
- [f602070f1e](https://github.com/Alfresco/alfresco-ng2-components/commit/f602070f1e) AAE-46388 Fix DependaBot config to ignore gh-aw-actions (#11946)
|
||||
- [d206d28f1c](https://github.com/Alfresco/alfresco-ng2-components/commit/d206d28f1c) [AAE-32905] Fix commit messages commands aren't working (#11922)
|
||||
- [80e9e51cfa](https://github.com/Alfresco/alfresco-ng2-components/commit/80e9e51cfa) AAE-46242 - Looks like we need to override the mapping as crowding config is not set to this format (#11939)
|
||||
- [345e1e69ad](https://github.com/Alfresco/alfresco-ng2-components/commit/345e1e69ad) AAE-46242 - should use the variable osx_locale to match the format xx-YY (#11936)
|
||||
- [a2b026ed7e](https://github.com/Alfresco/alfresco-ng2-components/commit/a2b026ed7e) [AAE-46242] - Changed the name convention for translation files to ISO standard naming (#11924)
|
||||
- [e40fd2250e](https://github.com/Alfresco/alfresco-ng2-components/commit/e40fd2250e) AAE-46388 Add Supply Chain Review agentic workflow (#11927)
|
||||
- [cfdd3f9c50](https://github.com/Alfresco/alfresco-ng2-components/commit/cfdd3f9c50) MD3 migration fixes (#11919)
|
||||
- [1314f0751f](https://github.com/Alfresco/alfresco-ng2-components/commit/1314f0751f) AAE-46476 Fix sorting functionality of task list retrieval from runtime bundle (#11921)
|
||||
- [f4882e99be](https://github.com/Alfresco/alfresco-ng2-components/commit/f4882e99be) Revert "New Crowdin translations by GitHub Action (#11796)" (#11923)
|
||||
- [b5d2de583d](https://github.com/Alfresco/alfresco-ng2-components/commit/b5d2de583d) build(deps): update 3rd party dependencies (#11916)
|
||||
- [70f90c65a5](https://github.com/Alfresco/alfresco-ng2-components/commit/70f90c65a5) AAE-46392 Fix card view select item crash on numeric initial value (#11917)
|
||||
- [f339647f40](https://github.com/Alfresco/alfresco-ng2-components/commit/f339647f40) refactor: migrate license checks off license-checker (#11905)
|
||||
- [a7fa68056d](https://github.com/Alfresco/alfresco-ng2-components/commit/a7fa68056d) chore: remove unused @nx/node dependency (#11895)
|
||||
- [8280aa2f6d](https://github.com/Alfresco/alfresco-ng2-components/commit/8280aa2f6d) chore: update NX packages to version 22.7.2 and related dependencies (#11904)
|
||||
- [5553aaaefe](https://github.com/Alfresco/alfresco-ng2-components/commit/5553aaaefe) [MNT-25522] Add support for fileLink icon (#11903)
|
||||
- [15814380a5](https://github.com/Alfresco/alfresco-ng2-components/commit/15814380a5) [ci:force] AAE-46219 [TEST] Revert cli build/installDeps changes from #11880 and #11899 (#11901)
|
||||
- [4044fafc39](https://github.com/Alfresco/alfresco-ng2-components/commit/4044fafc39) AAE-46219 Skip prod deps in cli:installDeps to unblock release (#11899)
|
||||
- [ece50ac32c](https://github.com/Alfresco/alfresco-ng2-components/commit/ece50ac32c) [MNT-25149] Card view select item multivalued property support (#11891)
|
||||
- [1d163c6749](https://github.com/Alfresco/alfresco-ng2-components/commit/1d163c6749) Revert "AAE-44065 Change date picker to match other styles (#11868)" (#11896)
|
||||
- [1fbd869902](https://github.com/Alfresco/alfresco-ng2-components/commit/1fbd869902) chore: remove unused eslint-plugin-prefer-arrow (#11894)
|
||||
- [165d0fc521](https://github.com/Alfresco/alfresco-ng2-components/commit/165d0fc521) chore: remove unused moment dependency (#11893)
|
||||
- [64d9f3a6d4](https://github.com/Alfresco/alfresco-ng2-components/commit/64d9f3a6d4) AAE-45774 Show error for date fields if invalid format (#11886)
|
||||
- [9e106b006c](https://github.com/Alfresco/alfresco-ng2-components/commit/9e106b006c) AAE-43881 Fix for form rules not applying when new [data] comes in (#11879)
|
||||
- [8309d2dc55](https://github.com/Alfresco/alfresco-ng2-components/commit/8309d2dc55) AAE-44065 Change date picker to match other styles (#11868)
|
||||
- [e3430ae36e](https://github.com/Alfresco/alfresco-ng2-components/commit/e3430ae36e) AAE-45695 Group @typescript-eslint/* packages in dependabot (#11880)
|
||||
- [1860fefd42](https://github.com/Alfresco/alfresco-ng2-components/commit/1860fefd42) [MNT-25647] Handle facets label wrapped in quotes (#11866)
|
||||
- [9fe4006a0d](https://github.com/Alfresco/alfresco-ng2-components/commit/9fe4006a0d) AAE-45391 Lazy loading and improvements for pdfJS (#11869)
|
||||
- [62fb51e910](https://github.com/Alfresco/alfresco-ng2-components/commit/62fb51e910) Update CODEOWNERS
|
||||
- [b7783d2259](https://github.com/Alfresco/alfresco-ng2-components/commit/b7783d2259) AAE-39830 Removing Material components color property (#11865)
|
||||
- [410490158f](https://github.com/Alfresco/alfresco-ng2-components/commit/410490158f) AAE-30881 Remove jsrassign (#11863)
|
||||
- [57bf066164](https://github.com/Alfresco/alfresco-ng2-components/commit/57bf066164) AAE-30882 Remove superagent for fetch (#11856)
|
||||
- [9cdacf3ebf](https://github.com/Alfresco/alfresco-ng2-components/commit/9cdacf3ebf) AAE-45312 Removing propagate job from release workflow (#11864)
|
||||
- [8821980913](https://github.com/Alfresco/alfresco-ng2-components/commit/8821980913) AAE-38566 Remove globals mixin (#11798)
|
||||
- [a73b100fae](https://github.com/Alfresco/alfresco-ng2-components/commit/a73b100fae) AAE-45003 Extend task cloud services (#11850)
|
||||
- [2649cdea2d](https://github.com/Alfresco/alfresco-ng2-components/commit/2649cdea2d) AAE-44003 Allow manual input for date field (#11830)
|
||||
- [3a543d8a0a](https://github.com/Alfresco/alfresco-ng2-components/commit/3a543d8a0a) AAE-32905 Rework pr pipeline (#11853)
|
||||
- [3fc59b2358](https://github.com/Alfresco/alfresco-ng2-components/commit/3fc59b2358) AAE-45216 Removing custom theme from core (#11862)
|
||||
- [337b82e988](https://github.com/Alfresco/alfresco-ng2-components/commit/337b82e988) AAE-45215 Removing unused breadcrumb theme (#11861)
|
||||
- [0faa2e1a56](https://github.com/Alfresco/alfresco-ng2-components/commit/0faa2e1a56) [AAE-0000] - fixed run for process-service-cloud (#11855)
|
||||
- [d6262eafc6](https://github.com/Alfresco/alfresco-ng2-components/commit/d6262eafc6) [ACS-11628] small for for init-acs-env.ts (#11860)
|
||||
- [d3ad66d25b](https://github.com/Alfresco/alfresco-ng2-components/commit/d3ad66d25b) AAE-44435 Fix incorrect Start process button on user task forms (#11837)
|
||||
- [aa2de3bde6](https://github.com/Alfresco/alfresco-ng2-components/commit/aa2de3bde6) AAE-45173 Adding background color to tooltip card (#11852)
|
||||
- [d1d06a68bc](https://github.com/Alfresco/alfresco-ng2-components/commit/d1d06a68bc) AAE-44098 Bottom tab navigation buttons (#11834)
|
||||
- [0a6da1ba13](https://github.com/Alfresco/alfresco-ng2-components/commit/0a6da1ba13) AAE-45076 Fix svg icon style (#11849)
|
||||
- [d3ac5362ba](https://github.com/Alfresco/alfresco-ng2-components/commit/d3ac5362ba) AAE-44946 - Sending also previous value to allow valuation and valida… (#11836)
|
||||
- [ddb8adc0e4](https://github.com/Alfresco/alfresco-ng2-components/commit/ddb8adc0e4) AAE-44752 Add type to ProcessInstanceCloud (#11847)
|
||||
- [85005c9c1c](https://github.com/Alfresco/alfresco-ng2-components/commit/85005c9c1c) AAE-44987 Applying color and background to sidenav (#11846)
|
||||
- [407c6c1829](https://github.com/Alfresco/alfresco-ng2-components/commit/407c6c1829) [ACS-11350] keyboard navigation user incorrect focus order after collapsing the left navigation menu (#11835)
|
||||
- [e3fc03c58d](https://github.com/Alfresco/alfresco-ng2-components/commit/e3fc03c58d) Revert "AAE-44098 Bottom Tab nav buttons (#11801)" (#11832)
|
||||
- [510a813ed2](https://github.com/Alfresco/alfresco-ng2-components/commit/510a813ed2) [ACS-11472] Fix file icon is not updated for version renditions (#11807)
|
||||
- [7864f1a8e0](https://github.com/Alfresco/alfresco-ng2-components/commit/7864f1a8e0) AAE-44098 Bottom Tab nav buttons (#11801)
|
||||
- [c09edd71ff](https://github.com/Alfresco/alfresco-ng2-components/commit/c09edd71ff) AAE-44761 Remove unused imports (#11829)
|
||||
- [64826c3a99](https://github.com/Alfresco/alfresco-ng2-components/commit/64826c3a99) AAE-40649 Material Design 3 migration (#11286)
|
||||
- [423c646bac](https://github.com/Alfresco/alfresco-ng2-components/commit/423c646bac) Post release version bump (#11828)
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
Title: Changelog for alfresco-ng2-components v9.0.0
|
||||
---
|
||||
|
||||
# Changelog
|
||||
|
||||
- [0a19be090e](git@github.com:Alfresco/alfresco-ng2-components/commit/0a19be090e) [ACS-12286] Pass includeFields through to getRecentFiles for -recent- (#12067)
|
||||
- [b69c3ae1ae](git@github.com:Alfresco/alfresco-ng2-components/commit/b69c3ae1ae) [MNT-25230] Displayed modified time for node version (#12066)
|
||||
- [ce907417b7](git@github.com:Alfresco/alfresco-ng2-components/commit/ce907417b7) AAE-48166 Compensate for VM/Citrix clock drift in token expiry check (#12024)
|
||||
- [4c7765e3fa](git@github.com:Alfresco/alfresco-ng2-components/commit/4c7765e3fa) AAE-46517 Add onRowCountChange form event for Repeatable Sections (#12062)
|
||||
- [407a64996e](git@github.com:Alfresco/alfresco-ng2-components/commit/407a64996e) AAE-46248 Format dropdown/radio option labels in display-text expressions (#12045)
|
||||
- [01cea60490](git@github.com:Alfresco/alfresco-ng2-components/commit/01cea60490) [MNT-25230] adw version history does not provide user information (#12050)
|
||||
- [09ef05038c](git@github.com:Alfresco/alfresco-ng2-components/commit/09ef05038c) chore: remove deprecated MaterialModule and its references (#12051)
|
||||
- [c7bd56f244](git@github.com:Alfresco/alfresco-ng2-components/commit/c7bd56f244) AAE-43974 Fix for alfresco-content-app e2e failure (#12055)
|
||||
- [b86e41515e](git@github.com:Alfresco/alfresco-ng2-components/commit/b86e41515e) [ACS-12037] Remove Knowledge Discovery from ADF (#12042)
|
||||
- [0b35118dee](git@github.com:Alfresco/alfresco-ng2-components/commit/0b35118dee) AAE-47880 UI breaks when pasting very large text into text field (#12043)
|
||||
- [fe2b3d85dd](git@github.com:Alfresco/alfresco-ng2-components/commit/fe2b3d85dd) [ACS-12108] Introduce signed commits into ADF workflows (#12040)
|
||||
- [b0dc85422c](git@github.com:Alfresco/alfresco-ng2-components/commit/b0dc85422c) Improvement/AAE-43974 Refactoring form fields (#11945)
|
||||
- [23f0392c12](git@github.com:Alfresco/alfresco-ng2-components/commit/23f0392c12) AAE-47634 Refactor: switch form service getTask to use runtime bundle API (#12020)
|
||||
- [997303e6a7](git@github.com:Alfresco/alfresco-ng2-components/commit/997303e6a7) [ACS-11325] remove chevron button from tab order in adf-tree (#12041)
|
||||
- [45d1818f04](git@github.com:Alfresco/alfresco-ng2-components/commit/45d1818f04) [ACS-12098] firefox headless race condition viewer fix (#12023)
|
||||
- [448e667c52](git@github.com:Alfresco/alfresco-ng2-components/commit/448e667c52) [MNT-25681] Content node selector panel filtering fixed (#12032)
|
||||
- [18e602aa85](git@github.com:Alfresco/alfresco-ng2-components/commit/18e602aa85) [PRODSEC-13253] Increased peerDependencies versions to allow to increase angular version in other projects (#12031)
|
||||
- [4cd8648a05](git@github.com:Alfresco/alfresco-ng2-components/commit/4cd8648a05) Adjust release triggers to use cache-writable token (#12030)
|
||||
- [7f57b3936b](git@github.com:Alfresco/alfresco-ng2-components/commit/7f57b3936b) AAE-39528 Session timeout features (#12008)
|
||||
- [5a30baad99](git@github.com:Alfresco/alfresco-ng2-components/commit/5a30baad99) Release cache read/write fix (#12029)
|
||||
- [5050c3d974](git@github.com:Alfresco/alfresco-ng2-components/commit/5050c3d974) [ACS-12041] Add debounceTime in PeopleWidgetComponent to limit API calls on every keystroke (#12027)
|
||||
- [d8b36606e2](git@github.com:Alfresco/alfresco-ng2-components/commit/d8b36606e2) [MNT-25681] Search refactoring and unification (#12019)
|
||||
- [37f8fe47ac](git@github.com:Alfresco/alfresco-ng2-components/commit/37f8fe47ac) AAE-46695 Upgrade to pnpm 11.9 (#12028)
|
||||
- [af0d1e4ead](git@github.com:Alfresco/alfresco-ng2-components/commit/af0d1e4ead) AAE-47093 Update Supply Chain Review GH AW to latest (#12025)
|
||||
- [bd7f393db9](git@github.com:Alfresco/alfresco-ng2-components/commit/bd7f393db9) AAE-47634 Fix block task claim checking status on Runtime Bundle (#12009)
|
||||
- [d2d6696c05](git@github.com:Alfresco/alfresco-ng2-components/commit/d2d6696c05) Migrate to Angular 20, TypeScript 5.9, and Angular Material 20 (#11657)
|
||||
- [7546aa91a3](git@github.com:Alfresco/alfresco-ng2-components/commit/7546aa91a3) AAE-47577 Form Spinner persists outside of Automate Form (#12005)
|
||||
- [74d67c39a0](git@github.com:Alfresco/alfresco-ng2-components/commit/74d67c39a0) [ACS-11317] a11y Fix: Tags interactive controls are not manageable via keyboard (#11845)
|
||||
- [20c20f2749](git@github.com:Alfresco/alfresco-ng2-components/commit/20c20f2749) AAE-46247 Add type-aware form field value adapter service (#11968)
|
||||
- [b498d5744c](git@github.com:Alfresco/alfresco-ng2-components/commit/b498d5744c) [AAE-47470] - Upgrade Pnpm to 11.8.0 (#12004)
|
||||
- [80eef35e1d](git@github.com:Alfresco/alfresco-ng2-components/commit/80eef35e1d) [ACS-11325] Add keyboard navigation and a11y support for adf-tree component (#11973)
|
||||
- [49a3f1e104](git@github.com:Alfresco/alfresco-ng2-components/commit/49a3f1e104) AAE-47412 Update suprocesses and linked processes for process instance model (#11996)
|
||||
- [0beaa18452](git@github.com:Alfresco/alfresco-ng2-components/commit/0beaa18452) [ACS-11973] Fix: context menu disappears during bulk upload (#11994)
|
||||
- [76fd461741](git@github.com:Alfresco/alfresco-ng2-components/commit/76fd461741) AAE-47042 Add auto grow support to multiline text widget (#11981)
|
||||
- [78697ee62a](git@github.com:Alfresco/alfresco-ng2-components/commit/78697ee62a) [AAE-0000] - Fixed js-api error index.js (#11970)
|
||||
- [43c5bc17fb](git@github.com:Alfresco/alfresco-ng2-components/commit/43c5bc17fb) [ACS-12020] Updated init-aps passwords with secret ones (#11993)
|
||||
- [cda20e0386](git@github.com:Alfresco/alfresco-ng2-components/commit/cda20e0386) Post-release version bump (#11988)
|
||||
- [8f5099e822](git@github.com:Alfresco/alfresco-ng2-components/commit/8f5099e822) [ACS-11928] convert ReadStream to Blob before appending to FormData (#11987)
|
||||
- [5dd709ed58](git@github.com:Alfresco/alfresco-ng2-components/commit/5dd709ed58) AAE-44809 Fix form multi-file attachment - document viewer not updated on file selection (#11980)
|
||||
- [9d353bf271](git@github.com:Alfresco/alfresco-ng2-components/commit/9d353bf271) [ACS-11974] [ACA] Upload new version button is no longer disabled when clicked once (#11977)
|
||||
- [633702860f](git@github.com:Alfresco/alfresco-ng2-components/commit/633702860f) Post-release version bump (#11984)
|
||||
- [bf5b3af285](git@github.com:Alfresco/alfresco-ng2-components/commit/bf5b3af285) AAE-47094 Add Stale PR Cleanup workflow (#11983)
|
||||
@@ -13,6 +13,50 @@ The authType parameter specifies the authentication method, with BASIC and OAUTH
|
||||
"authType": "OAUTH"
|
||||
}
|
||||
```
|
||||
|
||||
## Session Timeout
|
||||
|
||||
ADF can track user activity and show a countdown dialog before logging out an idle authenticated session. Register the feature with `provideSessionTimeout` in your application providers:
|
||||
|
||||
```ts
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
import { provideSessionTimeout } from '@alfresco/adf-core';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideSessionTimeout({
|
||||
enabled: true,
|
||||
idleTimeoutMs: 30 * 60 * 1000,
|
||||
dialogTimeoutMs: 60 * 1000
|
||||
})
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
The same values can be configured in `app.config.json` with the `sessionTimeout` key:
|
||||
|
||||
```json
|
||||
{
|
||||
"sessionTimeout": {
|
||||
"enabled": true,
|
||||
"idleTimeoutMs": 1800000,
|
||||
"dialogTimeoutMs": 60000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Registering `provideSessionTimeout` enables the feature: `enabled` defaults to `true`, so the countdown is active unless you set `enabled` to `false` in `app.config.json` or in the options passed to `provideSessionTimeout`. The default idle timeout is 30 minutes, and the default dialog timeout is 60 seconds. Values passed to `provideSessionTimeout` take precedence over values from `app.config.json`.
|
||||
|
||||
Applications can defer startup until another async condition is enabled:
|
||||
|
||||
```ts
|
||||
provideSessionTimeout({
|
||||
startWhen: () => featureService.isOn$('session-timeout-feature')
|
||||
});
|
||||
```
|
||||
|
||||
Whether the user clicks **Log out** in the countdown dialog or leaves it unanswered until the countdown elapses, the normal logout flow runs and the user is redirected to the configured IdP/login page. The same logout is broadcast to other tabs so every session ends together.
|
||||
|
||||
# OAuth2 Configuration
|
||||
OAuth2 is a protocol that allows the application to authorize operations without exposing user credentials. The configuration includes several parameters essential for setting up OAuth2 authentication.
|
||||
|
||||
|
||||
@@ -90,7 +90,6 @@ export class YourSearchComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.queryBuilder.resetToDefaults();
|
||||
this.queryBuilder.updated.subscribe(() => void this.queryBuilder.execute());
|
||||
this.queryBuilder.executed.subscribe((data) => {
|
||||
this.queryBuilder.paging.skipCount = 0;
|
||||
this.onSearchResultLoaded(data);
|
||||
@@ -103,7 +102,7 @@ export class YourSearchComponent implements OnInit {
|
||||
|
||||
onSearchQueryChanged(string: string) {
|
||||
this.queryBuilder.userQuery = decodeURIComponent(string);
|
||||
this.queryBuilder.update();
|
||||
void this.queryBuilder.execute();
|
||||
}
|
||||
|
||||
async onPaginationChanged(pagination: Pagination) {
|
||||
@@ -111,7 +110,7 @@ export class YourSearchComponent implements OnInit {
|
||||
maxItems: pagination.maxItems,
|
||||
skipCount: pagination.skipCount
|
||||
};
|
||||
this.queryBuilder.update();
|
||||
void this.queryBuilder.execute();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -16,6 +16,7 @@ This page describes how you can configure the search configuration.
|
||||
- [Steps Involved In Search Configuration](#steps-involved-in-search-configuration)
|
||||
- [Configuration](#configuration)
|
||||
- [Extra fields and filter queries](#extra-fields-and-filter-queries)
|
||||
- [Search modes and wildcards](#search-modes-and-wildcards)
|
||||
- [Sorting](#sorting)
|
||||
- [Categories and widgets](#categories-and-widgets)
|
||||
- [Facet Fields](#facet-fields)
|
||||
@@ -267,6 +268,44 @@ settings:
|
||||
|
||||
Note that the entries of the `filterQueries` array are joined using the `AND` operator.
|
||||
|
||||
### Search modes and wildcards
|
||||
|
||||
When a user types free text into a search input, the [Search Query Builder Service](../../content-services/services/search-query-builder.service.md) turns that text (its `userQuery`) into the final query according to the configured *search mode*:
|
||||
|
||||
- **regular** (default) - the user input is parsed into a field query. Each term is matched
|
||||
against the fields listed in the `app:fields` array (falling back to `cm:name` when it is not
|
||||
set), and a `*` wildcard is appended to each term when wildcards are enabled. Words that are
|
||||
exactly `AND` or `OR` are preserved as logical operators.
|
||||
- **formula** - the user input is passed through verbatim as an
|
||||
[AFTS](https://docs.alfresco.com/content-services/latest/develop/search-api/) expression. Use
|
||||
this mode when the caller already builds valid query syntax.
|
||||
|
||||
The `app:fields` entry lists the fields used to expand a `regular` user query:
|
||||
|
||||
```json
|
||||
{
|
||||
"search": {
|
||||
...
|
||||
"app:fields": ["cm:name", "cm:title", "cm:description"]
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For example, with the configuration above and wildcards enabled, the user query `report` is
|
||||
expanded to `(cm:name:"report*" OR cm:title:"report*" OR cm:description:"report*")`.
|
||||
|
||||
Wildcard matching is controlled by the top-level `search-wildcards-enabled` flag in
|
||||
`app.config.json` (default `true`). When set to `false`, terms are matched exactly and the
|
||||
trailing `*` is not added (this also disables the `searchPrefix`/`searchSuffix` of the
|
||||
[Search text component](../content-services/components/search-text.component.md)):
|
||||
|
||||
```json
|
||||
{
|
||||
"search-wildcards-enabled": false
|
||||
}
|
||||
```
|
||||
|
||||
### Sorting
|
||||
|
||||
The Sorting configuration section consists of two blocks:
|
||||
|
||||
@@ -61,8 +61,8 @@ backend services have been tested with each released version of ADF.
|
||||
|
||||
<!--7.0.0-alpha.3 start-->
|
||||
|
||||
- [AgentService](content-services/services/agent.service.md)
|
||||
- [SearchAiService](content-services/services/search-ai.service.md)
|
||||
- AgentService — removed, see [Notes](README.md#knowledge-discovery-removal-v900)
|
||||
- SearchAiService — removed, see [Notes](README.md#knowledge-discovery-removal-v900)
|
||||
|
||||
<!--7.0.0-alpha.3 end-->
|
||||
|
||||
|
||||
@@ -59,3 +59,5 @@ The pages linked below contain the audit for all third party dependencies of ADF
|
||||
- [ADF 8.2.1](audit-info-8.2.1.md)
|
||||
- [ADF 8.3.1](audit-info-8.3.1.md)
|
||||
- [ADF 8.4.1](audit-info-8.4.1.md)
|
||||
- [ADF 8.5.0](audit-info-8.5.0.md)
|
||||
- [ADF 9.0.0](audit-info-9.0.0.md)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
Title: Audit info, alfresco-ng2-components 8.4.1
|
||||
---
|
||||
|
||||
# Audit information for alfresco-ng2-components 8.5.0
|
||||
|
||||
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 |
|
||||
| --- | --- | --- |
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
Title: Audit info, alfresco-ng2-components 9.0.0
|
||||
---
|
||||
|
||||
# Audit information for alfresco-ng2-components 9.0.0
|
||||
|
||||
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: 297
|
||||
|
||||
## Libraries
|
||||
|
||||
| Severity | Module | Vulnerable versions |
|
||||
| --- | --- | --- |
|
||||
|
||||
|
||||
Generated
-732
@@ -1,732 +0,0 @@
|
||||
{
|
||||
"name": "@alfresco/adf-cli",
|
||||
"version": "8.5.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@alfresco/adf-cli",
|
||||
"version": "8.5.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@alfresco/js-api": ">=9.4.0",
|
||||
"ejs": "^3.1.10",
|
||||
"node-fetch": "^2.7.0",
|
||||
"spdx-license-list": "^5.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"adf": "bin/adf-cli",
|
||||
"adf-cli": "bin/adf-cli"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ejs": "^3.1.2",
|
||||
"@types/node": "^20.1.7",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@alfresco/js-api": {
|
||||
"version": "9.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@alfresco/js-api/-/js-api-9.4.0.tgz",
|
||||
"integrity": "sha512-2yRlZRZHA0AK1KZgtnpVkT1vUtoKYA8eWd0lrn1Wc6oSVwRW6rY2tzFab/44NZaGi+hgP6TkGhHPGcddavKn4g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"eventemitter3": "^5.0.1",
|
||||
"superagent": "^9.0.1",
|
||||
"tslib": "^2.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@paralleldrive/cuid2": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz",
|
||||
"integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "^1.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ejs": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz",
|
||||
"integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.19.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.17.tgz",
|
||||
"integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asap": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz",
|
||||
"integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bound": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"get-intrinsic": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/component-emitter": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
|
||||
"integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cookiejar": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
|
||||
"integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dezalgo": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
|
||||
"integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"asap": "^2.0.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ejs": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"jake": "^10.8.5"
|
||||
},
|
||||
"bin": {
|
||||
"ejs": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-safe-stringify": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
|
||||
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/filelist": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"minimatch": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/formidable": {
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz",
|
||||
"integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"dezalgo": "^1.0.4",
|
||||
"once": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/tunnckoCore/commissions"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/jake": {
|
||||
"version": "10.9.4",
|
||||
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
|
||||
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"async": "^3.2.6",
|
||||
"filelist": "^1.0.4",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"jake": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
|
||||
"integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "5.1.9",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
|
||||
"integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.15.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
|
||||
"integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-list": "^1.0.0",
|
||||
"side-channel-map": "^1.0.1",
|
||||
"side-channel-weakmap": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-list": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-map": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
||||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-weakmap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
||||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-map": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-license-list": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-list/-/spdx-license-list-5.0.0.tgz",
|
||||
"integrity": "sha512-N5u9tEFRBUzQDjMKRRt8SHxC/UaqYApPmdF4MMFnICQg3z52onNbnneuro/sWw2rd+eGu9agQOzUbD671Xia7Q==",
|
||||
"license": "CC0-1.0",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/superagent": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz",
|
||||
"integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==",
|
||||
"deprecated": "Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"component-emitter": "^1.3.0",
|
||||
"cookiejar": "^2.1.4",
|
||||
"debug": "^4.3.4",
|
||||
"fast-safe-stringify": "^2.1.1",
|
||||
"form-data": "^4.0.0",
|
||||
"formidable": "^3.5.1",
|
||||
"methods": "^1.1.2",
|
||||
"mime": "2.6.0",
|
||||
"qs": "^6.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/superagent/node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"license": "ISC"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@alfresco/adf-cli",
|
||||
"description": "Alfresco ADF cli and utils",
|
||||
"version": "8.5.0",
|
||||
"version": "9.0.0",
|
||||
"author": "Hyland Software, Inc. and its affiliates",
|
||||
"bin": {
|
||||
"adf-cli": "bin/adf-cli",
|
||||
@@ -22,7 +22,7 @@
|
||||
"unlink": "cd ../../dist/libs/cli && npm unlink"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alfresco/js-api": ">=9.4.0",
|
||||
"@alfresco/js-api": ">=9.5.0",
|
||||
"ejs": "^3.1.10",
|
||||
"node-fetch": "^2.7.0",
|
||||
"spdx-license-list": "^5.0.0"
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"cwd": "dist/libs/cli",
|
||||
"commands": [
|
||||
{
|
||||
"command": "npm publish --tag {args.tag}",
|
||||
"command": "pnpm publish --no-git-checks --tag {args.tag}",
|
||||
"forwardAllArgs": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -98,8 +98,8 @@ Options:
|
||||
const packageDir = path.dirname(packagePath);
|
||||
|
||||
// Use spawnSync with array arguments for safer command execution (prevents shell injection)
|
||||
// Cross-platform: npm is available on PATH on all platforms (Windows, macOS, Linux)
|
||||
const result = spawnSync('npm', ['audit', '--json', '--prod'], {
|
||||
// Cross-platform: pnpm is available on PATH on all platforms (Windows, macOS, Linux)
|
||||
const result = spawnSync('pnpm', ['audit', '--json', '--prod'], {
|
||||
cwd: packageDir,
|
||||
encoding: 'utf-8',
|
||||
// shell: false is the default and more secure (no shell interpretation)
|
||||
|
||||
+269
-140
@@ -28,12 +28,16 @@ interface InitAcsEnvArgs {
|
||||
username?: string;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
const MAX_RETRY = 10;
|
||||
let counter = 0;
|
||||
const TIMEOUT = 6000;
|
||||
const RETRY_DELAY_MS = 6000;
|
||||
const ACS_DEFAULT = require('./resources').ACS_DEFAULT;
|
||||
|
||||
let alfrescoJsApi: AlfrescoApi;
|
||||
let nodesApi: NodesApi;
|
||||
let uploadApi: UploadApi;
|
||||
let sharedlinksApi: SharedlinksApi;
|
||||
let favoritesApi: FavoritesApi;
|
||||
|
||||
/**
|
||||
* Init ACS environment command
|
||||
@@ -46,7 +50,6 @@ Usage: init-acs-env [options]
|
||||
Initialize ACS environment
|
||||
|
||||
Options:
|
||||
-v, --version Output the version number
|
||||
--host <host> Remote environment host
|
||||
--clientId <id> SSO client (default: "alfresco")
|
||||
-p, --password <pass> Password
|
||||
@@ -56,11 +59,6 @@ Options:
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (argv.includes('-v') || argv.includes('--version')) {
|
||||
console.log('0.1.0');
|
||||
exit(0);
|
||||
}
|
||||
|
||||
const { values } = parseArgs({
|
||||
args: argv.slice(2),
|
||||
options: {
|
||||
@@ -92,160 +90,293 @@ Options:
|
||||
|
||||
await checkEnv(opts);
|
||||
|
||||
logger.info(`***** Step initialize ACS *****`);
|
||||
logger.info('***** Step initialize ACS *****');
|
||||
await initializeDefaultFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup default files
|
||||
* Initialize default files. Creates the e2e folder and ensures each file
|
||||
* exists with its required state (locked, shared, favorite).
|
||||
* Idempotent: only creates/modifies what is missing.
|
||||
*/
|
||||
async function initializeDefaultFiles() {
|
||||
const e2eFolder = ACS_DEFAULT.e2eFolder;
|
||||
const parentFolder = await createFolder(e2eFolder.name, '-my-');
|
||||
const parentFolderId = parentFolder.entry.id;
|
||||
const e2eFolderName: string = ACS_DEFAULT.e2eFolder.name;
|
||||
|
||||
for (let j = 0; j < ACS_DEFAULT.files.length; j++) {
|
||||
const fileInfo = ACS_DEFAULT.files[j];
|
||||
switch (fileInfo.action) {
|
||||
case 'UPLOAD': {
|
||||
await uploadFile(fileInfo.name, parentFolderId);
|
||||
break;
|
||||
}
|
||||
case 'LOCK': {
|
||||
const fileToLock = await uploadFile(fileInfo.name, parentFolderId);
|
||||
await lockFile(fileToLock.entry.id);
|
||||
break;
|
||||
}
|
||||
case 'SHARE': {
|
||||
const fileToShare = await uploadFile(fileInfo.name, parentFolderId);
|
||||
await shareFile(fileToShare.entry.id);
|
||||
break;
|
||||
}
|
||||
case 'FAVORITE': {
|
||||
const fileToFav = await uploadFile(fileInfo.name, parentFolderId);
|
||||
await favoriteFile(fileToFav.entry.id);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
logger.error('No action found for file ', fileInfo.name, parentFolderId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create folder
|
||||
*
|
||||
* @param folderName folder name
|
||||
* @param parentId parent folder id
|
||||
*/
|
||||
async function createFolder(folderName: string, parentId: string) {
|
||||
let createdFolder: NodeEntry;
|
||||
const body = {
|
||||
name: folderName,
|
||||
nodeType: 'cm:folder'
|
||||
};
|
||||
let parentFolder: NodeEntry;
|
||||
try {
|
||||
createdFolder = await new NodesApi(alfrescoJsApi).createNode(parentId, body, { overwrite: true });
|
||||
parentFolder = await withRetry(() => ensureFolder(e2eFolderName, '-my-'), `ensure folder ${e2eFolderName}`);
|
||||
} catch (error: any) {
|
||||
logger.warn(`Skipping file initialization: test-data folder could not be created: ${formatError(error)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`Folder ${folderName} was created`);
|
||||
} catch (err) {
|
||||
if (err.status === 409) {
|
||||
const relativePath = `/${folderName}`;
|
||||
createdFolder = await new NodesApi(alfrescoJsApi).getNode('-my-', { relativePath });
|
||||
}
|
||||
const parentFolderId = getEntryId(parentFolder, `folder ${e2eFolderName}`);
|
||||
|
||||
for (const fileInfo of ACS_DEFAULT.files) {
|
||||
await withRetry(() => processFile(fileInfo, parentFolderId), `initialize ${fileInfo.name}`);
|
||||
}
|
||||
return createdFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload file
|
||||
*
|
||||
* @param fileName file name
|
||||
* @param fileDestination destination path
|
||||
* Process a single file: upload if missing, then apply its action (lock/share/favorite).
|
||||
* @param fileInfo file descriptor from ACS_DEFAULT
|
||||
* @param fileInfo.name file name
|
||||
* @param fileInfo.action action to apply (LOCK, SHARE, FAVORITE)
|
||||
* @param parentFolderId parent folder node id
|
||||
*/
|
||||
async function uploadFile(fileName: string, fileDestination: string): Promise<NodeEntry> {
|
||||
async function processFile(fileInfo: { name: string; action: string }, parentFolderId: string) {
|
||||
const existingNode = await findNodeByRelativePath(parentFolderId, fileInfo.name);
|
||||
|
||||
let nodeId: string;
|
||||
if (existingNode?.entry?.id) {
|
||||
logger.info(`File ${fileInfo.name} already exists, verifying required state.`);
|
||||
nodeId = existingNode.entry.id;
|
||||
} else {
|
||||
const createdNode = await uploadFile(fileInfo.name, parentFolderId);
|
||||
nodeId = getEntryId(createdNode, `file ${fileInfo.name}`);
|
||||
}
|
||||
|
||||
switch (fileInfo.action) {
|
||||
case 'LOCK':
|
||||
await ensureLocked(nodeId, fileInfo.name, existingNode?.entry?.isLocked);
|
||||
break;
|
||||
case 'SHARE':
|
||||
await ensureShared(nodeId, fileInfo.name);
|
||||
break;
|
||||
case 'FAVORITE':
|
||||
await ensureFavorite(nodeId, fileInfo.name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a folder exists under the given parent. Creates it if missing.
|
||||
* Handles 409 conflict (race condition) by fetching the existing folder.
|
||||
* @param folderName folder name
|
||||
* @param parentId parent node id
|
||||
* @returns the folder node entry
|
||||
*/
|
||||
async function ensureFolder(folderName: string, parentId: string): Promise<NodeEntry> {
|
||||
const existingFolder = await findNodeByRelativePath(parentId, folderName);
|
||||
|
||||
if (existingFolder?.entry?.id) {
|
||||
if (!existingFolder.entry.isFolder) {
|
||||
throw new Error(
|
||||
`Cannot use ${folderName} as test-data folder: a non-folder node with that name already exists (nodeType: ${existingFolder.entry.nodeType}, id: ${existingFolder.entry.id}).`
|
||||
);
|
||||
}
|
||||
logger.info(`Folder ${folderName} already exists.`);
|
||||
return existingFolder;
|
||||
}
|
||||
|
||||
try {
|
||||
const createdFolder = await nodesApi.createNode(parentId, { name: folderName, nodeType: 'cm:folder' }, { overwrite: true });
|
||||
logger.info(`Folder ${folderName} was created`);
|
||||
return createdFolder;
|
||||
} catch (error: any) {
|
||||
if (error?.status === 409) {
|
||||
const conflictingFolder = await findNodeByRelativePath(parentId, folderName);
|
||||
|
||||
if (conflictingFolder?.entry?.id) {
|
||||
if (!conflictingFolder.entry.isFolder) {
|
||||
throw new Error(
|
||||
`Cannot use ${folderName} as test-data folder: a non-folder node with that name already exists (nodeType: ${conflictingFolder.entry.nodeType}, id: ${conflictingFolder.entry.id}).`
|
||||
);
|
||||
}
|
||||
logger.info(`Folder ${folderName} already exists.`);
|
||||
return conflictingFolder;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Failed to ensure folder ${folderName}: ${formatError(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a node by relative path under a parent. Returns null if not found (404).
|
||||
* @param parentId parent node id
|
||||
* @param fileName relative path / file name
|
||||
* @returns the node entry or null if not found
|
||||
*/
|
||||
async function findNodeByRelativePath(parentId: string, fileName: string): Promise<NodeEntry | null> {
|
||||
try {
|
||||
return await nodesApi.getNode(parentId, { relativePath: `/${fileName}`, include: ['isLocked'] });
|
||||
} catch (error: any) {
|
||||
if (error?.status === 404) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new Error(`Failed to fetch ${fileName}: ${formatError(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a file to the given destination folder.
|
||||
* @param fileName file name
|
||||
* @param destinationId destination folder node id
|
||||
* @returns the uploaded node entry
|
||||
*/
|
||||
async function uploadFile(fileName: string, destinationId: string): Promise<NodeEntry> {
|
||||
const filePath = `../resources/content/${fileName}`;
|
||||
const file = createReadStream(path.join(__dirname, filePath));
|
||||
let uploadedFile: NodeEntry;
|
||||
|
||||
try {
|
||||
uploadedFile = await new UploadApi(alfrescoJsApi).uploadFile(file, '', fileDestination, null, {
|
||||
const uploadedFile = await uploadApi.uploadFile(file, '', destinationId, null, {
|
||||
name: fileName,
|
||||
nodeType: 'cm:content',
|
||||
renditions: 'doclib',
|
||||
overwrite: true
|
||||
});
|
||||
logger.info(`File ${fileName} was uploaded`);
|
||||
} catch (err) {
|
||||
logger.error(`Failed to upload file with error: `, err);
|
||||
return uploadedFile;
|
||||
} catch (error: any) {
|
||||
throw new Error(`Failed to upload ${fileName}: ${formatError(error)}`);
|
||||
}
|
||||
return uploadedFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock file node
|
||||
*
|
||||
* Ensure a file is locked. Skips if already locked.
|
||||
* @param nodeId node id
|
||||
* @param fileName file name (for logging)
|
||||
* @param isAlreadyLocked whether the node is already locked
|
||||
*/
|
||||
async function lockFile(nodeId: string): Promise<NodeEntry> {
|
||||
const data = {
|
||||
type: 'ALLOW_OWNER_CHANGES'
|
||||
};
|
||||
async function ensureLocked(nodeId: string, fileName: string, isAlreadyLocked = false) {
|
||||
if (isAlreadyLocked) {
|
||||
logger.info(`File ${fileName} is already locked.`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await new NodesApi(alfrescoJsApi).lockNode(nodeId, data);
|
||||
logger.info('File was locked');
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error('Failed to lock file with error: ', error);
|
||||
return null;
|
||||
await nodesApi.lockNode(nodeId, { type: 'ALLOW_OWNER_CHANGES' });
|
||||
logger.info(`File ${fileName} was locked`);
|
||||
} catch (error: any) {
|
||||
throw new Error(`Failed to lock ${fileName}: ${formatError(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Share file node
|
||||
*
|
||||
* Ensure a file is shared. Handles 409 (already shared) gracefully.
|
||||
* @param nodeId node id
|
||||
* @param fileName file name (for logging)
|
||||
*/
|
||||
async function shareFile(nodeId: string) {
|
||||
const data = {
|
||||
nodeId
|
||||
};
|
||||
async function ensureShared(nodeId: string, fileName: string) {
|
||||
try {
|
||||
await new SharedlinksApi(alfrescoJsApi).createSharedLink(data);
|
||||
logger.info('File was shared');
|
||||
} catch (error) {
|
||||
logger.error('Failed to share file with error: ', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Favorite file node
|
||||
*
|
||||
* @param nodeId node id
|
||||
*/
|
||||
async function favoriteFile(nodeId: string) {
|
||||
const data = {
|
||||
target: {
|
||||
['file']: {
|
||||
guid: nodeId
|
||||
}
|
||||
await sharedlinksApi.createSharedLink({ nodeId });
|
||||
logger.info(`File ${fileName} was shared`);
|
||||
} catch (error: any) {
|
||||
if (error?.status === 409) {
|
||||
logger.info(`File ${fileName} is already shared.`);
|
||||
return;
|
||||
}
|
||||
};
|
||||
try {
|
||||
await new FavoritesApi(alfrescoJsApi).createFavorite('-me-', data);
|
||||
logger.info('File was add to favorites');
|
||||
} catch (error) {
|
||||
logger.error('Failed to add the file to favorites with error: ', error);
|
||||
|
||||
throw new Error(`Failed to share ${fileName}: ${formatError(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check environment state
|
||||
*
|
||||
* @param opts command options
|
||||
* Ensure a file is favorite. Handles 409 (already favorite) gracefully.
|
||||
* @param nodeId node id
|
||||
* @param fileName file name (for logging)
|
||||
*/
|
||||
async function checkEnv(opts: InitAcsEnvArgs) {
|
||||
async function ensureFavorite(nodeId: string, fileName: string) {
|
||||
try {
|
||||
await favoritesApi.createFavorite('-me-', {
|
||||
target: {
|
||||
file: {
|
||||
guid: nodeId
|
||||
}
|
||||
}
|
||||
});
|
||||
logger.info(`File ${fileName} was added to favorites`);
|
||||
} catch (error: any) {
|
||||
if (error?.status === 409) {
|
||||
logger.info(`File ${fileName} is already a favorite.`);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(`Failed to favorite ${fileName}: ${formatError(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract entry id from a node response. Throws if missing.
|
||||
* @param nodeEntry node entry response
|
||||
* @param label label for error message
|
||||
* @returns the node id
|
||||
*/
|
||||
function getEntryId(nodeEntry: NodeEntry, label: string): string {
|
||||
const nodeId = nodeEntry?.entry?.id;
|
||||
|
||||
if (nodeId) {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
throw new Error(`Missing ACS response entry for ${label}.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format an error for logging.
|
||||
* @param error error object
|
||||
* @returns formatted error string
|
||||
*/
|
||||
function formatError(error: any): string {
|
||||
if (!error) {
|
||||
return 'Unknown error';
|
||||
}
|
||||
|
||||
if (typeof error === 'string') {
|
||||
return error;
|
||||
}
|
||||
|
||||
try {
|
||||
return error?.message || error?.stack || JSON.stringify(error);
|
||||
} catch {
|
||||
return 'Unknown error (unable to serialize)';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry wrapper for transient failures.
|
||||
* @param fn async function to execute
|
||||
* @param label label for logging
|
||||
* @param maxAttempts maximum retry attempts
|
||||
* @returns the result of the function
|
||||
*/
|
||||
async function withRetry<T>(fn: () => Promise<T>, label: string, maxAttempts = 3): Promise<T> {
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (error: any) {
|
||||
if (attempt === maxAttempts) {
|
||||
logger.error(`${label}: failed after ${maxAttempts} attempts: ${formatError(error)}`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
logger.warn(`${label}: attempt ${attempt} failed, retrying in ${RETRY_DELAY_MS / 1000}s: ${formatError(error)}`);
|
||||
await wait(RETRY_DELAY_MS);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`${label}: exhausted all ${maxAttempts} attempts`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async delay.
|
||||
* @param ms milliseconds to wait
|
||||
* @returns a promise that resolves after the delay
|
||||
*/
|
||||
function wait(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check environment state and authenticate. Retries on transient failures.
|
||||
* @param opts command options
|
||||
* @param attempt current attempt number
|
||||
*/
|
||||
async function checkEnv(opts: InitAcsEnvArgs, attempt = 1) {
|
||||
try {
|
||||
alfrescoJsApi = new AlfrescoApi({
|
||||
provider: 'ALL',
|
||||
@@ -260,31 +391,29 @@ async function checkEnv(opts: InitAcsEnvArgs) {
|
||||
},
|
||||
contextRoot: 'alfresco'
|
||||
});
|
||||
|
||||
await alfrescoJsApi.login(opts.username, opts.password);
|
||||
} catch (e) {
|
||||
if (e.error.code === 'ETIMEDOUT') {
|
||||
logger.error('The env is not reachable. Terminating');
|
||||
exit(1);
|
||||
|
||||
nodesApi = new NodesApi(alfrescoJsApi);
|
||||
uploadApi = new UploadApi(alfrescoJsApi);
|
||||
sharedlinksApi = new SharedlinksApi(alfrescoJsApi);
|
||||
favoritesApi = new FavoritesApi(alfrescoJsApi);
|
||||
} catch (error: any) {
|
||||
const errorCode = error?.error?.code;
|
||||
|
||||
if (errorCode === 'ETIMEDOUT' || error?.status === 504) {
|
||||
logger.warn('Login attempt timed out or received a gateway error, environment may still be starting up.');
|
||||
} else {
|
||||
logger.error('Login error, environment down or inaccessible.');
|
||||
}
|
||||
logger.error('Login error environment down or inaccessible');
|
||||
counter++;
|
||||
if (MAX_RETRY === counter) {
|
||||
|
||||
if (attempt >= MAX_RETRY) {
|
||||
logger.error('Give up');
|
||||
exit(1);
|
||||
} else {
|
||||
logger.error(`Retry in 1 minute attempt N ${counter}`);
|
||||
sleep(TIMEOUT);
|
||||
await checkEnv(opts);
|
||||
}
|
||||
|
||||
logger.warn(`Retry in ${RETRY_DELAY_MS / 1000} seconds, attempt ${attempt}`);
|
||||
await wait(RETRY_DELAY_MS);
|
||||
await checkEnv(opts, attempt + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a delay
|
||||
*
|
||||
* @param delay timeout in milliseconds
|
||||
*/
|
||||
function sleep(delay: number) {
|
||||
const start = new Date().getTime();
|
||||
while (new Date().getTime() < start + delay) {}
|
||||
}
|
||||
|
||||
+151
-87
@@ -30,8 +30,11 @@ import { parseArgs } from 'node:util';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { createReadStream } from 'node:fs';
|
||||
import * as path from 'path';
|
||||
import { config } from 'dotenv';
|
||||
import { logger } from './logger';
|
||||
|
||||
config({ path: path.resolve(__dirname, '../../../.env') });
|
||||
|
||||
interface InitApsEnvArgs {
|
||||
host?: string;
|
||||
clientId?: string;
|
||||
@@ -40,12 +43,12 @@ interface InitApsEnvArgs {
|
||||
license?: string;
|
||||
}
|
||||
const MAX_RETRY = 10;
|
||||
let counter = 0;
|
||||
const TIMEOUT = 6000;
|
||||
const RETRY_DELAY_MS = 6000;
|
||||
const TENANT_DEFAULT_ID = 1;
|
||||
const TENANT_DEFAULT_NAME = 'default';
|
||||
const CONTENT_DEFAULT_NAME = 'adw-content';
|
||||
const ACTIVITI_APPS = require('./resources').ACTIVITI_APPS;
|
||||
const E2E_USER_PASSWORD = process.env.HR_USER_PASSWORD;
|
||||
|
||||
let alfrescoJsApi: AlfrescoApi;
|
||||
|
||||
@@ -60,7 +63,6 @@ Usage: init-aps-env [options]
|
||||
Initialize APS environment
|
||||
|
||||
Options:
|
||||
-v, --version Output the version number
|
||||
--host <host> Remote environment host
|
||||
--clientId <id> SSO client (default: "alfresco")
|
||||
-p, --password <pass> Password
|
||||
@@ -71,11 +73,6 @@ Options:
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (argv.includes('-v') || argv.includes('--version')) {
|
||||
console.log('0.1.0');
|
||||
exit(0);
|
||||
}
|
||||
|
||||
const { values } = parseArgs({
|
||||
args: argv.slice(2),
|
||||
options: {
|
||||
@@ -111,6 +108,14 @@ Options:
|
||||
|
||||
await checkEnv(opts);
|
||||
|
||||
const e2eAppReady = await ensureE2eApplicationDeployed();
|
||||
if (e2eAppReady) {
|
||||
logger.info(`APS environment already initialized (terraform). Skipping.`);
|
||||
return;
|
||||
}
|
||||
|
||||
await alfrescoJsApi.login(opts.username, opts.password);
|
||||
|
||||
logger.info(`***** Step 1 - Check License *****`);
|
||||
|
||||
let licenceUploaded = false;
|
||||
@@ -146,25 +151,24 @@ Options:
|
||||
logger.info(`***** Step 4 - Create users *****`);
|
||||
const users = await getDefaultApsUsersFromRealm(opts);
|
||||
if (tenantId && users && users.length > 0) {
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
await createUsers(tenantId, users[i]);
|
||||
for (const user of users) {
|
||||
await createUsers(tenantId, user);
|
||||
}
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
logger.info('Impersonate user: ' + users[i].username);
|
||||
await alfrescoJsApi.login(users[i].username, 'password');
|
||||
await authorizeUserToContentRepo(opts, users[i]);
|
||||
for (const user of users) {
|
||||
logger.info('Impersonate user: ' + user.username);
|
||||
await alfrescoJsApi.login(user.username, E2E_USER_PASSWORD);
|
||||
await authorizeUserToContentRepo(opts, user);
|
||||
|
||||
const defaultUser = 'hruser';
|
||||
if (users[i].username.includes(defaultUser)) {
|
||||
logger.info(`***** Step initialize APS apps for user ${defaultUser} *****`);
|
||||
if (user.username.includes('hruser')) {
|
||||
logger.info(`***** Step initialize APS apps for user hruser *****`);
|
||||
await initializeDefaultApps();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.info('Something went wrong. Was not able to create the users');
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Aps something went wrong. Tenant id ${tenantId}`, error);
|
||||
} catch (error: any) {
|
||||
logger.error(`Aps something went wrong. Tenant id ${tenantId}: ${formatError(error)}`);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
@@ -173,15 +177,56 @@ Options:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure e2e-Application is deployed for hruser.
|
||||
* If the app is already present, returns true (skip full init).
|
||||
* If hruser can log in but the app is missing, imports, publishes and deploys it.
|
||||
* Returns false only if hruser cannot log in or deployment fails.
|
||||
* @returns `true` if app is deployed, otherwise `false`
|
||||
*/
|
||||
async function ensureE2eApplicationDeployed(): Promise<boolean> {
|
||||
try {
|
||||
await alfrescoJsApi.login('hruser', E2E_USER_PASSWORD);
|
||||
const runtimeAppDefinitionsApi = new RuntimeAppDefinitionsApi(alfrescoJsApi);
|
||||
const availableApps = await runtimeAppDefinitionsApi.getAppDefinitions();
|
||||
const e2eApp = availableApps.data?.filter((app) => app.name?.includes('e2e-Application'));
|
||||
if (e2eApp && e2eApp.length > 0) {
|
||||
logger.info(`e2e-Application is already deployed for hruser`);
|
||||
return true;
|
||||
}
|
||||
logger.info(`e2e-Application not found for hruser - uploading and deploying it now`);
|
||||
const appDefinition = await importPublishApp('e2e-Application');
|
||||
if (appDefinition?.appDefinition?.id) {
|
||||
await deployApp(appDefinition.appDefinition.id);
|
||||
const verifyApps = await runtimeAppDefinitionsApi.getAppDefinitions();
|
||||
const deployed = verifyApps.data?.some((app) => app.name?.includes('e2e-Application'));
|
||||
if (deployed) {
|
||||
logger.info(`e2e-Application successfully deployed for hruser`);
|
||||
return true;
|
||||
}
|
||||
logger.info(`e2e-Application deployment could not be verified - proceeding with full initialization`);
|
||||
return false;
|
||||
}
|
||||
logger.info(`Failed to import/deploy e2e-Application for hruser - proceeding with full initialization`);
|
||||
return false;
|
||||
} catch (error: any) {
|
||||
logger.info(`Unable to verify APS state for hruser - proceeding with initialization: ${formatError(error)}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise default applications
|
||||
*/
|
||||
async function initializeDefaultApps() {
|
||||
for (let x = 0; x < ACTIVITI_APPS.apps.length; x++) {
|
||||
const appInfo = ACTIVITI_APPS.apps[x];
|
||||
for (const appInfo of ACTIVITI_APPS.apps) {
|
||||
const isDeployed = await isDefaultAppDeployed(appInfo.name);
|
||||
if (isDeployed !== undefined && !isDeployed) {
|
||||
const appDefinition = await importPublishApp(`${appInfo.name}`);
|
||||
if (!isDeployed) {
|
||||
const appDefinition = await importPublishApp(appInfo.name);
|
||||
if (!appDefinition?.appDefinition?.id) {
|
||||
logger.error(`Failed to import app ${appInfo.name}, skipping deployment.`);
|
||||
continue;
|
||||
}
|
||||
await deployApp(appDefinition.appDefinition.id);
|
||||
} else {
|
||||
logger.info(`***** App ${appInfo.name} already deployed *****`);
|
||||
@@ -190,11 +235,11 @@ async function initializeDefaultApps() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check environment
|
||||
*
|
||||
* Check environment state and authenticate. Retries on transient failures.
|
||||
* @param opts command options
|
||||
* @param attempt current attempt number
|
||||
*/
|
||||
async function checkEnv(opts: InitApsEnvArgs) {
|
||||
async function checkEnv(opts: InitApsEnvArgs, attempt = 1) {
|
||||
try {
|
||||
alfrescoJsApi = new AlfrescoApi({
|
||||
provider: 'ALL',
|
||||
@@ -210,27 +255,28 @@ async function checkEnv(opts: InitApsEnvArgs) {
|
||||
}
|
||||
});
|
||||
await alfrescoJsApi.login(opts.username, opts.password);
|
||||
} catch (e) {
|
||||
if (e.error.code === 'ETIMEDOUT') {
|
||||
logger.error('The env is not reachable. Terminating');
|
||||
exit(1);
|
||||
} catch (error: any) {
|
||||
const errorCode = error?.error?.code;
|
||||
|
||||
if (errorCode === 'ETIMEDOUT' || error?.status === 504) {
|
||||
logger.warn('Login attempt timed out or received a gateway error, environment may still be starting up.');
|
||||
} else {
|
||||
logger.error('Login error, environment down or inaccessible.');
|
||||
}
|
||||
logger.info('Login error environment down or inaccessible');
|
||||
counter++;
|
||||
if (MAX_RETRY === counter) {
|
||||
|
||||
if (attempt >= MAX_RETRY) {
|
||||
logger.error('Give up');
|
||||
exit(1);
|
||||
} else {
|
||||
logger.error(`Retry in 1 minute attempt N ${counter}`);
|
||||
sleep(TIMEOUT);
|
||||
await checkEnv(opts);
|
||||
}
|
||||
|
||||
logger.warn(`Retry in ${RETRY_DELAY_MS / 1000} seconds, attempt ${attempt}`);
|
||||
await wait(RETRY_DELAY_MS);
|
||||
await checkEnv(opts, attempt + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the default tenant is present
|
||||
*
|
||||
* @param tenantId tenant id
|
||||
* @param tenantName tenant name
|
||||
* @returns `true` if tenant is found, otherwise `false`
|
||||
@@ -241,7 +287,7 @@ async function hasDefaultTenant(tenantId: number, tenantName: string): Promise<b
|
||||
try {
|
||||
const adminTenantsApi = new AdminTenantsApi(alfrescoJsApi);
|
||||
tenant = await adminTenantsApi.getTenant(tenantId);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
logger.info(`Aps: does not have tenant with id: ${tenantId}`);
|
||||
return false;
|
||||
}
|
||||
@@ -258,8 +304,8 @@ async function hasDefaultTenant(tenantId: number, tenantName: string): Promise<b
|
||||
|
||||
/**
|
||||
* Create default tenant
|
||||
*
|
||||
* @param tenantName tenant name
|
||||
* @returns the tenant id or null
|
||||
*/
|
||||
async function createDefaultTenant(tenantName: string) {
|
||||
const tenantPost = {
|
||||
@@ -273,28 +319,27 @@ async function createDefaultTenant(tenantName: string) {
|
||||
const tenant = await adminTenantsApi.createTenant(tenantPost);
|
||||
logger.info(`APS: Tenant ${tenantName} created with id: ${tenant.id}`);
|
||||
return tenant.id;
|
||||
} catch (error) {
|
||||
logger.info(`APS: not able to create the default tenant: ${JSON.parse(error.message)}`);
|
||||
} catch (error: any) {
|
||||
logger.info(`APS: not able to create the default tenant: ${formatError(error)}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create users
|
||||
*
|
||||
* @param tenantId tenant id
|
||||
* @param user user object
|
||||
* @returns the created user
|
||||
*/
|
||||
async function createUsers(tenantId: number, user: any) {
|
||||
logger.info(`Create user ${user.email} on tenant: ${tenantId}`);
|
||||
const passwordCamelCase = 'Password';
|
||||
const userJson = new UserRepresentation({
|
||||
email: user.email,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
status: 'active',
|
||||
type: 'enterprise',
|
||||
password: passwordCamelCase,
|
||||
password: E2E_USER_PASSWORD,
|
||||
tenantId
|
||||
});
|
||||
|
||||
@@ -303,15 +348,15 @@ async function createUsers(tenantId: number, user: any) {
|
||||
const userInfo = await adminUsersApi.createNewUser(userJson);
|
||||
logger.info(`APS: User ${userInfo.email} created with id: ${userInfo.id}`);
|
||||
return user;
|
||||
} catch (error) {
|
||||
logger.info(`APS: not able to create the default user: ${error.message}`);
|
||||
} catch (error: any) {
|
||||
logger.info(`APS: not able to create the default user: ${formatError(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Activiti license
|
||||
*
|
||||
* @param opts command options
|
||||
* @returns `true` if license uploaded successfully, otherwise `false`
|
||||
*/
|
||||
async function updateLicense(opts: InitApsEnvArgs) {
|
||||
const fileContent = createReadStream(path.join(__dirname, '/activiti.lic'));
|
||||
@@ -330,15 +375,14 @@ async function updateLicense(opts: InitApsEnvArgs) {
|
||||
);
|
||||
logger.info(`Aps License uploaded!`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error(`Aps License failed!`, error.message);
|
||||
} catch (error: any) {
|
||||
logger.error(`Aps License failed! ${formatError(error)}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if default application is deployed
|
||||
*
|
||||
* @param appName application name
|
||||
* @returns `true` if application is deployed, otherwise `false`
|
||||
*/
|
||||
@@ -349,18 +393,18 @@ async function isDefaultAppDeployed(appName: string): Promise<boolean> {
|
||||
const availableApps = await runtimeAppDefinitionsApi.getAppDefinitions();
|
||||
const defaultApp = availableApps.data?.filter((app) => app.name?.includes(appName));
|
||||
return defaultApp && defaultApp.length > 0;
|
||||
} catch (error) {
|
||||
logger.error(`Aps app failed to import/Publish!`);
|
||||
} catch (error: any) {
|
||||
logger.error(`Failed to check if ${appName} is deployed: ${formatError(error)}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import and publish the application
|
||||
*
|
||||
* @param appName application name
|
||||
* @returns the app definition result
|
||||
*/
|
||||
async function importPublishApp(appName: string): Promise<AppDefinitionUpdateResultRepresentation> {
|
||||
async function importPublishApp(appName: string): Promise<AppDefinitionUpdateResultRepresentation | null> {
|
||||
const appNameExtension = `../resources/${appName}.zip`;
|
||||
logger.info(`Import app ${appNameExtension}`);
|
||||
const pathFile = path.join(__dirname, appNameExtension);
|
||||
@@ -371,15 +415,14 @@ async function importPublishApp(appName: string): Promise<AppDefinitionUpdateRes
|
||||
const result = await appDefinitionsApi.importAndPublishApp(fileContent, { renewIdmEntries: true });
|
||||
logger.info(`Aps app imported and published!`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error(`Aps app failed to import/Publish!`, error.message);
|
||||
} catch (error: any) {
|
||||
logger.error(`Aps app failed to import/Publish! ${formatError(error)}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy application
|
||||
*
|
||||
* @param appDefinitionId app definition id
|
||||
*/
|
||||
async function deployApp(appDefinitionId: number) {
|
||||
@@ -392,15 +435,15 @@ async function deployApp(appDefinitionId: number) {
|
||||
const runtimeAppDefinitionsApi = new RuntimeAppDefinitionsApi(alfrescoJsApi);
|
||||
await runtimeAppDefinitionsApi.deployAppDefinitions(body);
|
||||
logger.info(`Aps app deployed`);
|
||||
} catch (error) {
|
||||
logger.error(`Aps app failed to deploy!`);
|
||||
} catch (error: any) {
|
||||
logger.error(`Aps app failed to deploy: ${formatError(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if Activiti app has license
|
||||
*
|
||||
* @param opts command options
|
||||
* @returns `true` if license is valid, otherwise `false`
|
||||
*/
|
||||
async function hasLicense(opts: InitApsEnvArgs): Promise<boolean> {
|
||||
try {
|
||||
@@ -415,13 +458,13 @@ async function hasLicense(opts: InitApsEnvArgs): Promise<boolean> {
|
||||
['application/json'],
|
||||
['application/json']
|
||||
);
|
||||
if (license && license.status === 'valid') {
|
||||
if (license?.status === 'valid') {
|
||||
logger.info(`Aps has a valid License!`);
|
||||
return true;
|
||||
}
|
||||
logger.info(`Aps does NOT have a valid License!`);
|
||||
return false;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
logger.error(`Aps not able to check the license`);
|
||||
return false;
|
||||
}
|
||||
@@ -429,8 +472,8 @@ async function hasLicense(opts: InitApsEnvArgs): Promise<boolean> {
|
||||
|
||||
/**
|
||||
* Get default users from the realm
|
||||
*
|
||||
* @param opts command options
|
||||
* @returns array of default APS users or null
|
||||
*/
|
||||
async function getDefaultApsUsersFromRealm(opts: InitApsEnvArgs) {
|
||||
try {
|
||||
@@ -449,18 +492,18 @@ async function getDefaultApsUsersFromRealm(opts: InitApsEnvArgs) {
|
||||
const apsDefaultUsers = users.filter((user) => usernamesOfApsDefaultUsers.includes(user.username));
|
||||
logger.info(`Keycloak found ${apsDefaultUsers.length} users`);
|
||||
return apsDefaultUsers;
|
||||
} catch (error) {
|
||||
logger.error(`APS: not able to fetch user: ${error.message}`);
|
||||
} catch (error: any) {
|
||||
logger.error(`APS: not able to fetch user: ${formatError(error)}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that ACS repo for Activiti is present
|
||||
*
|
||||
* @param opts command options
|
||||
* @param tenantId tenant id
|
||||
* @param contentName content service name
|
||||
* @returns `true` if content repo is present, otherwise `false`
|
||||
*/
|
||||
async function isContentRepoPresent(opts: InitApsEnvArgs, tenantId: number, contentName: string): Promise<boolean> {
|
||||
try {
|
||||
@@ -476,18 +519,18 @@ async function isContentRepoPresent(opts: InitApsEnvArgs, tenantId: number, cont
|
||||
['application/json']
|
||||
);
|
||||
return !!contentRepos.data.find((repo) => repo.name === contentName);
|
||||
} catch (error) {
|
||||
logger.error(`APS: not able to create content: ${error.message}`);
|
||||
return null;
|
||||
} catch (error: any) {
|
||||
logger.error(`APS: not able to check content repo: ${formatError(error)}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add content service with basic auth
|
||||
*
|
||||
* @param opts command options
|
||||
* @param tenantId tenant id
|
||||
* @param name content name
|
||||
* @returns the created content repo
|
||||
*/
|
||||
async function addContentRepoWithBasic(opts: InitApsEnvArgs, tenantId: number, name: string) {
|
||||
logger.info(`Create Content with name ${name} and basic auth`);
|
||||
@@ -517,14 +560,13 @@ async function addContentRepoWithBasic(opts: InitApsEnvArgs, tenantId: number, n
|
||||
);
|
||||
logger.info(`Content created!`);
|
||||
return content;
|
||||
} catch (error) {
|
||||
logger.error(`APS: not able to create content: ${error.message}`);
|
||||
} catch (error: any) {
|
||||
logger.error(`APS: not able to create content: ${formatError(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize activiti user to ACS repo
|
||||
*
|
||||
* @param opts command options
|
||||
* @param user user object
|
||||
*/
|
||||
@@ -551,22 +593,22 @@ async function authorizeUserToContentRepo(opts: InitApsEnvArgs, user: any) {
|
||||
}
|
||||
}
|
||||
return;
|
||||
} catch (error) {
|
||||
logger.error(`APS: not able to authorize content: ${error.message}`);
|
||||
} catch (error: any) {
|
||||
logger.error(`APS: not able to authorize content: ${formatError(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize user with content using basic auth
|
||||
*
|
||||
* @param opts command options
|
||||
* @param username username
|
||||
* @param contentId content id
|
||||
* @returns the authorized content
|
||||
*/
|
||||
async function authorizeUserToContentWithBasic(opts: InitApsEnvArgs, username: string, contentId: string) {
|
||||
logger.info(`Authorize ${username} on contentId: ${contentId} in basic auth`);
|
||||
try {
|
||||
const body = { username, password: 'password' };
|
||||
const body = { username, password: E2E_USER_PASSWORD };
|
||||
const content = await alfrescoJsApi.oauth2Auth.callCustomApi(
|
||||
`${opts.host}/activiti-app/api/enterprise/integration/alfresco/${contentId}/account`,
|
||||
'POST',
|
||||
@@ -580,15 +622,15 @@ async function authorizeUserToContentWithBasic(opts: InitApsEnvArgs, username: s
|
||||
);
|
||||
logger.info(`User authorized!`);
|
||||
return content;
|
||||
} catch (error) {
|
||||
logger.error(`APS: not able to authorize content: ${error.message}`);
|
||||
} catch (error: any) {
|
||||
logger.error(`APS: not able to authorize content: ${formatError(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download APS license file
|
||||
*
|
||||
* @param apsLicensePath path to license file
|
||||
* @returns `true` if download succeeded, otherwise `false`
|
||||
*/
|
||||
async function downloadLicenseFile(apsLicensePath: string) {
|
||||
const args = [`s3`, `cp`, apsLicensePath, `./`];
|
||||
@@ -606,11 +648,33 @@ async function downloadLicenseFile(apsLicensePath: string) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a delay
|
||||
*
|
||||
* @param delay timeout in milliseconds
|
||||
* Format an error for logging.
|
||||
* @param error error object
|
||||
* @returns formatted error string
|
||||
*/
|
||||
function sleep(delay: number) {
|
||||
const start = new Date().getTime();
|
||||
while (new Date().getTime() < start + delay) {}
|
||||
function formatError(error: any): string {
|
||||
if (!error) {
|
||||
return 'Unknown error';
|
||||
}
|
||||
|
||||
if (typeof error === 'string') {
|
||||
return error;
|
||||
}
|
||||
|
||||
try {
|
||||
return error?.message || error?.stack || JSON.stringify(error);
|
||||
} catch {
|
||||
return 'Unknown error (unable to serialize)';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async delay.
|
||||
* @param ms milliseconds to wait
|
||||
* @returns a promise that resolves after the delay
|
||||
*/
|
||||
function wait(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@alfresco/adf-content-services",
|
||||
"description": "Alfresco ADF content services",
|
||||
"version": "8.5.0",
|
||||
"version": "9.0.0",
|
||||
"author": "Hyland Software, Inc. and its affiliates",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -11,19 +11,19 @@
|
||||
"url": "https://github.com/Alfresco/alfresco-ng2-components/issues"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/animations": ">=14.1.3",
|
||||
"@angular/cdk": ">=14.1.2",
|
||||
"@angular/common": ">=14.1.3",
|
||||
"@angular/compiler": ">=14.1.3",
|
||||
"@angular/core": ">=14.1.3",
|
||||
"@angular/forms": ">=14.1.3",
|
||||
"@angular/material": ">=14.1.2",
|
||||
"@angular/platform-browser": ">=14.1.3",
|
||||
"@angular/platform-browser-dynamic": ">=14.1.3",
|
||||
"@angular/router": ">=14.1.3",
|
||||
"@alfresco/js-api": ">=9.4.1",
|
||||
"@angular/animations": ">=20.3.25",
|
||||
"@angular/cdk": ">=20.2.14",
|
||||
"@angular/common": ">=20.3.25",
|
||||
"@angular/compiler": ">=20.3.25",
|
||||
"@angular/core": ">=20.3.25",
|
||||
"@angular/forms": ">=20.3.25",
|
||||
"@angular/material": ">=20.2.14",
|
||||
"@angular/platform-browser": ">=20.3.25",
|
||||
"@angular/platform-browser-dynamic": ">=20.3.25",
|
||||
"@angular/router": ">=20.3.25",
|
||||
"@alfresco/js-api": ">=10.0.0",
|
||||
"@ngx-translate/core": ">=17.0.0",
|
||||
"@alfresco/adf-core": ">=8.4.1"
|
||||
"@alfresco/adf-core": ">=9.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"content-services",
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
"cwd": "dist/libs/content-services",
|
||||
"commands": [
|
||||
{
|
||||
"command": "npm publish --tag {args.tag}",
|
||||
"command": "pnpm publish --no-git-checks --tag {args.tag}",
|
||||
"forwardAllArgs": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
@mixin adf-error-icon {
|
||||
font-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* @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.
|
||||
*/
|
||||
|
||||
export * from './public-api';
|
||||
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* @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.
|
||||
*/
|
||||
|
||||
export * from './services/agent.service';
|
||||
@@ -1,67 +0,0 @@
|
||||
/*!
|
||||
* @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 { TestBed } from '@angular/core/testing';
|
||||
import { AgentService } from './agent.service';
|
||||
import { Agent, AgentPaging } from '@alfresco/js-api';
|
||||
|
||||
const agent1: Agent = {
|
||||
id: '1',
|
||||
name: 'HR Agent',
|
||||
description: 'Your Claims Doc Agent streamlines the extraction, analysis, and management of data from insurance claims documents.',
|
||||
avatarUrl: ''
|
||||
};
|
||||
|
||||
const agent2: Agent = {
|
||||
id: '2',
|
||||
name: 'Policy Agent',
|
||||
description: 'Your Claims Doc Agent streamlines the extraction, analysis, and management of data from insurance claims documents.',
|
||||
avatarUrl: ''
|
||||
};
|
||||
|
||||
const agentPagingObjectMock: AgentPaging = {
|
||||
list: {
|
||||
entries: [
|
||||
{
|
||||
entry: agent1
|
||||
},
|
||||
{
|
||||
entry: agent2
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const agentListMock: Agent[] = [agent1, agent2];
|
||||
|
||||
describe('AgentService', () => {
|
||||
let agentService: AgentService;
|
||||
|
||||
beforeEach(() => {
|
||||
agentService = TestBed.inject(AgentService);
|
||||
});
|
||||
|
||||
it('should load agents', (done) => {
|
||||
spyOn(agentService.agentsApi, 'getAgents').and.returnValue(Promise.resolve(agentPagingObjectMock));
|
||||
|
||||
agentService.getAgents().subscribe((pagingResponse) => {
|
||||
expect(pagingResponse).toEqual(agentListMock);
|
||||
expect(agentService.agentsApi.getAgents).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
/*!
|
||||
* @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 { Injectable, inject } from '@angular/core';
|
||||
import { Agent, AgentsApi, LazyApi } from '@alfresco/js-api';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { AlfrescoApiService } from '../../services';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AgentService {
|
||||
private readonly apiService = inject(AlfrescoApiService);
|
||||
|
||||
private readonly agents = new BehaviorSubject<Agent[]>([]);
|
||||
|
||||
@LazyApi((self: AgentService) => new AgentsApi(self.apiService.getInstance()))
|
||||
declare readonly agentsApi: AgentsApi;
|
||||
|
||||
agents$ = this.agents.asObservable();
|
||||
|
||||
/**
|
||||
* Gets all agents from cache. If cache is empty, fetches agents from backend.
|
||||
*
|
||||
* @returns Agent[] list containing agents.
|
||||
*/
|
||||
getAgents(): Observable<Agent[]> {
|
||||
return this.agents$.pipe(
|
||||
switchMap((agentsList) => {
|
||||
if (agentsList.length) {
|
||||
return of(agentsList);
|
||||
}
|
||||
return from(this.agentsApi.getAgents()).pipe(
|
||||
map((paging) => {
|
||||
const agentEntries = paging.list.entries.map((agentEntry) => agentEntry.entry);
|
||||
this.agents.next(agentEntries);
|
||||
return agentEntries;
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -206,5 +206,9 @@ describe('ContentNodeDialogService', () => {
|
||||
expect(testContentNodeSelectorComponentData.isSelectionValid(testData.node)).toBe(testData.expected);
|
||||
});
|
||||
});
|
||||
|
||||
it('should scope the search to folders by disabling files in the result', () => {
|
||||
expect(testContentNodeSelectorComponentData.showFilesInResult).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -154,7 +154,8 @@ export class ContentNodeDialogService {
|
||||
where: '(isFolder=true)',
|
||||
isSelectionValid: this.isCopyMoveSelectionValid.bind(this),
|
||||
excludeSiteContent: excludeSiteContent || ContentNodeDialogService.nonDocumentSiteContent,
|
||||
select
|
||||
select,
|
||||
showFilesInResult: false
|
||||
};
|
||||
|
||||
const dialogRef = this.openContentNodeDialog(data, 'adf-content-node-selector-dialog', '630px');
|
||||
@@ -196,7 +197,8 @@ export class ContentNodeDialogService {
|
||||
imageResolver: this.imageResolver.bind(this),
|
||||
isSelectionValid: this.hasAllowableOperationsOnNodeFolder.bind(this),
|
||||
where: '(isFolder=true)',
|
||||
select
|
||||
select,
|
||||
showFilesInResult: false
|
||||
};
|
||||
|
||||
const dialogRef = this.openContentNodeDialog(data, 'adf-content-node-selector-dialog', '630px');
|
||||
|
||||
+143
-71
@@ -19,14 +19,13 @@ import { DebugElement } from '@angular/core';
|
||||
import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Node, NodeEntry, NodePaging, RequestScope, ResultSetPaging, SiteEntry, SitePaging, SitePagingList } from '@alfresco/js-api';
|
||||
import { of } from 'rxjs';
|
||||
import { of, Subject } from 'rxjs';
|
||||
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component';
|
||||
import { DocumentListService } from '../../document-list/services/document-list.service';
|
||||
import { DocumentListComponent } from '../../document-list/components/document-list.component';
|
||||
import { CustomResourcesService } from '../../document-list/services/custom-resources.service';
|
||||
import { NodeEntryEvent, ShareDataRow } from '../../document-list';
|
||||
import { SearchQueryBuilderService } from '../../search';
|
||||
import { mockSearchRequest } from '../../mock/search-query.mock';
|
||||
import { SitesService } from '../../common/services/sites.service';
|
||||
import { NodesApiService } from '../../common/services/nodes-api.service';
|
||||
import { UnitTestingUtils } from '../../../../../core/src/lib/testing/unit-testing-utils';
|
||||
@@ -161,43 +160,70 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
});
|
||||
|
||||
it('should the user query get updated when the user types in the search input', fakeAsync(() => {
|
||||
const updateSpy = spyOn(searchQueryBuilderService, 'update');
|
||||
typeToSearchBox('search-term');
|
||||
|
||||
tick(debounceSearch);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(updateSpy).toHaveBeenCalled();
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(searchQueryBuilderService.userQuery).toEqual('(search-term*)');
|
||||
expect(component.searchTerm).toEqual('search-term');
|
||||
}));
|
||||
|
||||
it('should set the query builder search mode to formula when the user types in the search input', fakeAsync(() => {
|
||||
typeToSearchBox('search-term');
|
||||
|
||||
tick(debounceSearch);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(searchQueryBuilderService.searchMode).toEqual('formula');
|
||||
}));
|
||||
|
||||
it('should add the wildcard suffix to the user query when wildcards are enabled', fakeAsync(() => {
|
||||
spyOnProperty(searchQueryBuilderService, 'wildcardsEnabled', 'get').and.returnValue(true);
|
||||
typeToSearchBox('search-term');
|
||||
|
||||
tick(debounceSearch);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(searchQueryBuilderService.userQuery).toEqual('(search-term*)');
|
||||
}));
|
||||
|
||||
it('should NOT add the wildcard suffix to the user query when wildcards are disabled', fakeAsync(() => {
|
||||
spyOnProperty(searchQueryBuilderService, 'wildcardsEnabled', 'get').and.returnValue(false);
|
||||
typeToSearchBox('search-term');
|
||||
|
||||
tick(debounceSearch);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(searchQueryBuilderService.userQuery).toEqual('(search-term)');
|
||||
}));
|
||||
|
||||
it('should perform a search when the search request gets updated and it is defined', fakeAsync(() => {
|
||||
typeToSearchBox('search-term');
|
||||
|
||||
tick(debounceSearch);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(searchSpy).toHaveBeenCalledWith(false, mockSearchRequest);
|
||||
expect(searchSpy).toHaveBeenCalledWith(false);
|
||||
}));
|
||||
|
||||
it('should NOT perform a search and clear the results when the search request gets updated and it is NOT defined', async () => {
|
||||
it('should NOT perform a search and clear the results when the search input is empty', fakeAsync(() => {
|
||||
spyOn(component, 'clearSearch');
|
||||
|
||||
searchQueryBuilderService.userQuery = '';
|
||||
searchQueryBuilderService.update();
|
||||
typeToSearchBox('');
|
||||
|
||||
tick(debounceSearch);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(searchSpy).not.toHaveBeenCalled();
|
||||
expect(component.clearSearch).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reset the search term when clicking the clear icon', async () => {
|
||||
component.searchTerm = 'search-term';
|
||||
searchQueryBuilderService.userQuery = 'search-term';
|
||||
spyOn(component, 'clearSearch');
|
||||
spyOn(component, 'clear').and.callThrough();
|
||||
|
||||
fixture.detectChanges();
|
||||
const clearIcon = getSearchIcon('clear');
|
||||
@@ -207,7 +233,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
|
||||
expect(searchQueryBuilderService.userQuery).toEqual('');
|
||||
expect(component.searchTerm).toEqual('');
|
||||
expect(component.clearSearch).toHaveBeenCalled();
|
||||
expect(component.clear).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should load the results by calling the search api on search change', fakeAsync(() => {
|
||||
@@ -216,22 +242,21 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
tick(debounceSearch);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(searchSpy).toHaveBeenCalledWith(false, mockSearchRequest);
|
||||
expect(searchSpy).toHaveBeenCalledWith(false);
|
||||
}));
|
||||
|
||||
it('should the query include the show files filterQuery', fakeAsync(() => {
|
||||
spyOn(searchQueryBuilderService, 'addFilterQuery').and.callThrough();
|
||||
component.showFilesInResult = true;
|
||||
typeToSearchBox('search-term');
|
||||
|
||||
const expectedRequest = mockSearchRequest;
|
||||
expectedRequest.filterQueries.push({
|
||||
query: `TYPE:'cm:folder' OR TYPE:'cm:content'`
|
||||
});
|
||||
const expectedRequest = `TYPE:'cm:folder' OR TYPE:'cm:content'`;
|
||||
|
||||
tick(debounceSearch);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(searchSpy).toHaveBeenCalledWith(false, expectedRequest);
|
||||
expect(searchSpy).toHaveBeenCalledWith(false);
|
||||
expect(searchQueryBuilderService.addFilterQuery).toHaveBeenCalledWith(expectedRequest);
|
||||
}));
|
||||
|
||||
it('should reset the currently chosen node in case of starting a new search', fakeAsync(() => {
|
||||
@@ -253,6 +278,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
});
|
||||
|
||||
it('should perform a search when selecting a site with the correct query', fakeAsync(() => {
|
||||
spyOn(searchQueryBuilderService, 'addFilterQuery').and.callThrough();
|
||||
typeToSearchBox('search-term');
|
||||
|
||||
tick(debounceSearch);
|
||||
@@ -261,14 +287,41 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
|
||||
component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry);
|
||||
|
||||
const expectedRequest = mockSearchRequest;
|
||||
expectedRequest.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/namek'` }];
|
||||
const expectedRequest = `ANCESTOR:'workspace://SpacesStore/namek'`;
|
||||
|
||||
expect(searchSpy.calls.count()).toBe(2);
|
||||
expect(searchSpy).toHaveBeenCalledWith(false, expectedRequest);
|
||||
expect(searchSpy).toHaveBeenCalledWith(false);
|
||||
expect(searchQueryBuilderService.addFilterQuery).toHaveBeenCalledWith(expectedRequest);
|
||||
}));
|
||||
|
||||
it('should remove the previous site filter query when changing the selected site', fakeAsync(() => {
|
||||
spyOn(searchQueryBuilderService, 'addFilterQuery').and.callThrough();
|
||||
spyOn(searchQueryBuilderService, 'removeFilterQuery').and.callThrough();
|
||||
typeToSearchBox('search-term');
|
||||
tick(debounceSearch);
|
||||
|
||||
component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry);
|
||||
component.siteChanged({ entry: { guid: 'vegeta' } } as SiteEntry);
|
||||
|
||||
expect(searchQueryBuilderService.removeFilterQuery).toHaveBeenCalledWith(`ANCESTOR:'workspace://SpacesStore/namek'`);
|
||||
expect(searchQueryBuilderService.addFilterQuery).toHaveBeenCalledWith(`ANCESTOR:'workspace://SpacesStore/vegeta'`);
|
||||
expect(searchQueryBuilderService.filterQueries).toEqual([{ query: `ANCESTOR:'workspace://SpacesStore/vegeta'` }]);
|
||||
}));
|
||||
|
||||
it('should browse the selected site folder without searching when no search term is present', fakeAsync(() => {
|
||||
expect(searchSpy.calls.count()).toBe(0);
|
||||
|
||||
component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry);
|
||||
tick(debounceSearch);
|
||||
|
||||
expect(searchSpy).not.toHaveBeenCalled();
|
||||
expect(component.searchTerm).toBe('');
|
||||
expect(component.folderIdToShow).toBe('namek');
|
||||
expect(component.showingSearchResults).toBe(false);
|
||||
}));
|
||||
|
||||
it('should create the query with the right parameters on changing the site selectBox value from a custom dropdown menu', fakeAsync(() => {
|
||||
spyOn(searchQueryBuilderService, 'addFilterQuery').and.callThrough();
|
||||
component.dropdownSiteList = { list: { entries: [{ entry: { guid: '-sites-' } }, { entry: { guid: 'namek' } }] } } as SitePaging;
|
||||
component.documentList.folderNode = { id: 'fakeNodeId', isFolder: true, path: {} } as Node;
|
||||
fixture.detectChanges();
|
||||
@@ -281,17 +334,12 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
|
||||
component.siteChanged({ entry: { guid: '-sites-' } } as SiteEntry);
|
||||
|
||||
const expectedRequest = mockSearchRequest;
|
||||
expectedRequest.filterQueries = [
|
||||
{
|
||||
query: `ANCESTOR:'workspace://SpacesStore/-sites-' OR ANCESTOR:'workspace://SpacesStore/123456testId' OR ANCESTOR:'workspace://SpacesStore/09876543testId'`
|
||||
}
|
||||
];
|
||||
const expectedRequest = `ANCESTOR:'workspace://SpacesStore/-sites-' OR ANCESTOR:'workspace://SpacesStore/123456testId' OR ANCESTOR:'workspace://SpacesStore/09876543testId'`;
|
||||
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(searchSpy.calls.count()).toBe(2);
|
||||
expect(searchSpy).toHaveBeenCalledWith(false, mockSearchRequest);
|
||||
expect(searchSpy).toHaveBeenCalledWith(false, expectedRequest);
|
||||
expect(searchSpy).toHaveBeenCalledWith(false);
|
||||
expect(searchQueryBuilderService.addFilterQuery).toHaveBeenCalledWith(expectedRequest);
|
||||
}));
|
||||
|
||||
it('should get the corresponding node ids on search when a known alias is selected from dropdown', fakeAsync(() => {
|
||||
@@ -321,6 +369,29 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
expect(getCorrespondingNodeIdsSpy.calls.mostRecent().args[0]).toEqual('-sites-');
|
||||
}));
|
||||
|
||||
it('should execute the search only after the corresponding node ids filter query is applied for aliases', fakeAsync(() => {
|
||||
component.documentList.folderNode = { id: 'fakeNodeId', isFolder: true, path: {} } as Node;
|
||||
spyOn(searchQueryBuilderService, 'addFilterQuery').and.callThrough();
|
||||
const nodeIds$ = new Subject<string[]>();
|
||||
getCorrespondingNodeIdsSpy.and.returnValue(nodeIds$.asObservable());
|
||||
|
||||
typeToSearchBox('vegeta');
|
||||
tick(debounceSearch);
|
||||
|
||||
searchSpy.calls.reset();
|
||||
|
||||
component.siteChanged({ entry: { guid: '-sites-' } } as SiteEntry);
|
||||
|
||||
expect(searchSpy).not.toHaveBeenCalled();
|
||||
|
||||
nodeIds$.next(['123456testId']);
|
||||
nodeIds$.complete();
|
||||
|
||||
const expectedRequest = `ANCESTOR:'workspace://SpacesStore/-sites-' OR ANCESTOR:'workspace://SpacesStore/123456testId'`;
|
||||
expect(searchQueryBuilderService.addFilterQuery).toHaveBeenCalledWith(expectedRequest);
|
||||
expect(searchSpy).toHaveBeenCalledWith(false);
|
||||
}));
|
||||
|
||||
it('should NOT get the corresponding node ids on search when NOTHING is selected from dropdown', fakeAsync(() => {
|
||||
component.dropdownSiteList = { list: { entries: [{ entry: { guid: '-sites-' } }, { entry: { guid: 'namek' } }] } } as SitePaging;
|
||||
fixture.detectChanges();
|
||||
@@ -395,7 +466,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
expect(component.clear).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should clear the search field, nodes and chosenNode when clicking on the X (clear) icon', async () => {
|
||||
it('should reset the search term and user query when clearing the search', async () => {
|
||||
component.chosenNode = [entry];
|
||||
|
||||
component.nodePaging = {
|
||||
@@ -404,32 +475,31 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
}
|
||||
};
|
||||
component.searchTerm = 'piccolo';
|
||||
searchQueryBuilderService.userQuery = 'piccolo';
|
||||
component.showingSearchResults = true;
|
||||
|
||||
component.clear();
|
||||
|
||||
expect(component.searchTerm).toBe('');
|
||||
expect(component.nodePaging).toEqual(null);
|
||||
expect(component.chosenNode).toBeNull();
|
||||
expect(component.showingSearchResults).toBeFalsy();
|
||||
expect(searchQueryBuilderService.userQuery).toBe('');
|
||||
expect(searchSpy).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('should the query restrict the search to the currentFolderId in case is defined', fakeAsync(() => {
|
||||
spyOn(searchQueryBuilderService, 'addFilterQuery').and.callThrough();
|
||||
component.currentFolderId = 'my-root-id';
|
||||
component.restrictRootToCurrentFolderId = true;
|
||||
component.ngOnInit();
|
||||
typeToSearchBox('search-term');
|
||||
tick(debounceSearch);
|
||||
|
||||
const expectedRequest = mockSearchRequest;
|
||||
expectedRequest.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/my-root-id'` }];
|
||||
const expectedRequest = `ANCESTOR:'workspace://SpacesStore/my-root-id'`;
|
||||
|
||||
expect(searchSpy).toHaveBeenCalledWith(false, expectedRequest);
|
||||
expect(searchSpy).toHaveBeenCalledWith(false);
|
||||
expect(searchQueryBuilderService.addFilterQuery).toHaveBeenCalledWith(expectedRequest);
|
||||
}));
|
||||
|
||||
it('should emit showingSearch event with true while searching', async () => {
|
||||
searchQueryBuilderService.userQuery = 'mock-search-term';
|
||||
searchQueryBuilderService.update();
|
||||
spyOn(customResourcesService, 'hasCorrespondingNodeIds').and.returnValue(true);
|
||||
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
|
||||
|
||||
@@ -454,8 +524,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
expect(showingSearchSpy).toHaveBeenCalledWith(false);
|
||||
}));
|
||||
|
||||
it('should emit showingResults event with false when clicking on the X (clear) icon', async () => {
|
||||
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
|
||||
it('should re-run the query with an empty user query when clicking on the X (clear) icon', async () => {
|
||||
component.chosenNode = [entry];
|
||||
|
||||
component.nodePaging = {
|
||||
@@ -464,17 +533,16 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
}
|
||||
};
|
||||
component.searchTerm = 'piccolo';
|
||||
component.showingSearchResults = true;
|
||||
searchQueryBuilderService.userQuery = 'piccolo';
|
||||
|
||||
component.clear();
|
||||
|
||||
expect(component.showingSearchResults).toBe(false);
|
||||
expect(showingSearchSpy).toHaveBeenCalledWith(false);
|
||||
expect(component.searchTerm).toBe('');
|
||||
expect(searchQueryBuilderService.userQuery).toBe('');
|
||||
expect(searchSpy).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('should emit showingResults event with false if search api fails', async () => {
|
||||
searchQueryBuilderService.userQuery = 'mock-search-term';
|
||||
searchQueryBuilderService.update();
|
||||
getCorrespondingNodeIdsSpy.and.throwError('Failed');
|
||||
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
|
||||
await searchQueryBuilderService.execute(true, { query: { query: 'search' } });
|
||||
@@ -488,15 +556,17 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
});
|
||||
|
||||
it('should the query restrict the search to the site and not to the currentFolderId in case is changed', async () => {
|
||||
spyOn(searchQueryBuilderService, 'addFilterQuery').and.callThrough();
|
||||
component.searchTerm = 'search-term';
|
||||
searchQueryBuilderService.userQuery = 'search-term*';
|
||||
component.currentFolderId = 'my-root-id';
|
||||
component.restrictRootToCurrentFolderId = true;
|
||||
component.siteChanged({ entry: { guid: 'my-site-id' } } as SiteEntry);
|
||||
|
||||
const expectedRequest = mockSearchRequest;
|
||||
expectedRequest.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/my-site-id'` }];
|
||||
const expectedRequest = `ANCESTOR:'workspace://SpacesStore/my-site-id'`;
|
||||
|
||||
expect(searchSpy).toHaveBeenCalledWith(false, expectedRequest);
|
||||
expect(searchSpy).toHaveBeenCalledWith(false);
|
||||
expect(searchQueryBuilderService.addFilterQuery).toHaveBeenCalledWith(expectedRequest);
|
||||
});
|
||||
|
||||
it('should restrict the breadcrumb to the currentFolderId in case restrictedRoot is true', async () => {
|
||||
@@ -563,10 +633,12 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
|
||||
expect(searchSpy.calls.count()).toBe(2);
|
||||
|
||||
component.clear();
|
||||
typeToSearchBox('');
|
||||
tick(debounceSearch);
|
||||
|
||||
expect(component.searchTerm).toBe('');
|
||||
expect(component.folderIdToShow).toBe('namek');
|
||||
flush();
|
||||
}));
|
||||
|
||||
it('should show the current folder content instead of search results if search was not performed', async () => {
|
||||
@@ -638,25 +710,23 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
fixture.detectChanges();
|
||||
const documentList = testingUtils.getByCSS('[data-automation-id="content-node-selector-document-list"]');
|
||||
expect(documentList).not.toBeNull();
|
||||
expect(component.hasValidQuery).toEqual(true);
|
||||
expect(documentList.componentInstance.currentFolderId).toBeNull();
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should not show the result list when results are returned but there is no search term typed', (done) => {
|
||||
searchQueryBuilderService.userQuery = '';
|
||||
searchQueryBuilderService.update();
|
||||
it('should not run a search nor show the result list when there is no search term typed', fakeAsync(() => {
|
||||
spyOn(component, 'clearSearch').and.callThrough();
|
||||
|
||||
setTimeout(() => {
|
||||
triggerSearchResults(fakeResultSetPaging);
|
||||
fixture.detectChanges();
|
||||
typeToSearchBox('');
|
||||
tick(debounceSearch);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.hasValidQuery).toEqual(false);
|
||||
expect(component.showingSearchResults).toEqual(false);
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
expect(searchSpy).not.toHaveBeenCalled();
|
||||
expect(component.clearSearch).toHaveBeenCalled();
|
||||
expect(component.showingSearchResults).toEqual(false);
|
||||
flush();
|
||||
}));
|
||||
|
||||
it('should highlight the results when search was performed in the next timeframe', (done) => {
|
||||
typeToSearchBox('My');
|
||||
@@ -673,7 +743,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
}, 300);
|
||||
});
|
||||
|
||||
it('should show the default text instead of result list if search was cleared', (done) => {
|
||||
it('should reset the search term and re-run an empty query when the clear button is clicked', (done) => {
|
||||
typeToSearchBox();
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -683,12 +753,13 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
fixture.whenStable().then(() => {
|
||||
const clearButton = getSearchIcon('clear');
|
||||
expect(clearButton).not.toBeNull();
|
||||
searchSpy.calls.reset();
|
||||
clearButton.triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
|
||||
const documentList = testingUtils.getByCSS('[data-automation-id="content-node-selector-document-list"]');
|
||||
expect(documentList).not.toBeNull();
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
expect(component.searchTerm).toBe('');
|
||||
expect(searchQueryBuilderService.userQuery).toBe('');
|
||||
expect(searchSpy).toHaveBeenCalledWith(false);
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
@@ -709,20 +780,20 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
flush();
|
||||
}));
|
||||
|
||||
it('should set the folderIdToShow to the default "currentFolderId" if siteId is undefined', (done) => {
|
||||
it('should reset the folder to the selected site and fall back to currentFolderId when site is undefined', () => {
|
||||
component.siteChanged({ entry: { guid: 'Kame-Sennin Muten Roshi' } } as SiteEntry);
|
||||
component.resetFolderToShow();
|
||||
fixture.detectChanges();
|
||||
|
||||
let documentList = testingUtils.getByCSS('[data-automation-id="content-node-selector-document-list"]');
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('Kame-Sennin Muten Roshi');
|
||||
|
||||
component.siteChanged({ entry: { guid: undefined } } as SiteEntry);
|
||||
component.resetFolderToShow();
|
||||
fixture.detectChanges();
|
||||
|
||||
documentList = testingUtils.getByCSS('[data-automation-id="content-node-selector-document-list"]');
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
describe('Pagination "Load more" button', () => {
|
||||
@@ -752,7 +823,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
});
|
||||
|
||||
it('should set its loading state to true to perform a new search', async () => {
|
||||
component.prepareDialogForNewSearch(mockSearchRequest);
|
||||
component.prepareDialogForNewSearch();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
@@ -764,13 +835,14 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
expect(paginationLoading).not.toBeNull();
|
||||
});
|
||||
|
||||
it('Should infinite pagination target be null when we use it for search ', fakeAsync(() => {
|
||||
it('Should keep the document list as infinite pagination target while searching', fakeAsync(() => {
|
||||
component.showingSearchResults = true;
|
||||
typeToSearchBox('shenron');
|
||||
tick(debounceSearch);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.target).toBeNull();
|
||||
expect(component.target).toEqual(component.documentList);
|
||||
flush();
|
||||
}));
|
||||
|
||||
it('Should infinite pagination target be present when search finish', () => {
|
||||
|
||||
-3
@@ -270,7 +270,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
|
||||
it('should not show the breadcrumb if search was performed as last action', async () => {
|
||||
searchQueryBuilderService.userQuery = 'mock-search-term';
|
||||
searchQueryBuilderService.update();
|
||||
triggerSearchResults(fakeResultSetPaging);
|
||||
|
||||
fixture.detectChanges();
|
||||
@@ -291,7 +290,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
|
||||
it('should show the breadcrumb in search results for a valid node selection', async () => {
|
||||
searchQueryBuilderService.userQuery = 'mock-search-term';
|
||||
searchQueryBuilderService.update();
|
||||
triggerSearchResults(fakeResultSetPaging);
|
||||
|
||||
const chosenNode = new Node({ path: { elements: [{ name: 'one' }] } });
|
||||
@@ -306,7 +304,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
|
||||
it('should show the breadcrumb in search results even for an invalid node selection', async () => {
|
||||
component.isSelectionValid = (node: Node) => node.isFile;
|
||||
searchQueryBuilderService.userQuery = 'mock-search-term';
|
||||
searchQueryBuilderService.update();
|
||||
triggerSearchResults(fakeResultSetPaging);
|
||||
|
||||
const chosenNode = new Node({ path: { elements: [{ name: 'fake-path' }] }, isFile: false, isFolder: true });
|
||||
|
||||
+57
-48
@@ -33,14 +33,15 @@ import {
|
||||
} from '@alfresco/adf-core';
|
||||
import { FileUploadCompleteEvent, FileUploadDeleteEvent, NodesApiService, SitesService, UploadService } from '../../common';
|
||||
import { ReactiveFormsModule, UntypedFormControl } from '@angular/forms';
|
||||
import { Node, NodeEntry, NodePaging, Pagination, RequestScope, SearchRequest, SiteEntry, SitePaging } from '@alfresco/js-api';
|
||||
import { Node, NodeEntry, NodePaging, Pagination, RequestScope, SiteEntry, SitePaging } from '@alfresco/js-api';
|
||||
import { DocumentListComponent } from '../../document-list/components/document-list.component';
|
||||
import { RowFilter } from '../../document-list/data/row-filter.model';
|
||||
import { ImageResolver } from '../../document-list/data/image-resolver.model';
|
||||
import { CustomResourcesService } from '../../document-list/services/custom-resources.service';
|
||||
import { ShareDataRow } from '../../document-list/data/share-data-row.model';
|
||||
import { NodeEntryEvent } from '../../document-list/components/node.event';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { debounceTime, map } from 'rxjs/operators';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { ContentNodeSelectorPanelService } from './content-node-selector-panel.service';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
@@ -110,6 +111,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit {
|
||||
private showSearchField = true;
|
||||
private showCounter = false;
|
||||
private _emptyList = true;
|
||||
private lastParentFilterQuery: string | null = null;
|
||||
|
||||
/** If true will restrict the search and breadcrumbs to the currentFolderId */
|
||||
@Input()
|
||||
@@ -292,7 +294,6 @@ export class ContentNodeSelectorPanelComponent implements OnInit {
|
||||
folderIdToShow: string | null = null;
|
||||
breadcrumbFolderTitle: string | null = null;
|
||||
startSiteGuid: string | null = null;
|
||||
hasValidQuery: boolean = false;
|
||||
showHeader = ShowHeaderMode.Never;
|
||||
|
||||
@ViewChild(InfinitePaginationComponent, { static: true })
|
||||
@@ -334,26 +335,16 @@ export class ContentNodeSelectorPanelComponent implements OnInit {
|
||||
.pipe(debounceTime(this.debounceSearch), takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe((searchValue: string) => {
|
||||
this.searchTerm = searchValue;
|
||||
this.queryBuilderService.userQuery = searchValue.length > 0 ? `${searchValue}*` : searchValue;
|
||||
this.queryBuilderService.update();
|
||||
if (this.searchTerm) {
|
||||
this.executeSearch(searchValue);
|
||||
} else {
|
||||
this.resetFolderToShow();
|
||||
this.clearSearch();
|
||||
}
|
||||
});
|
||||
|
||||
this.queryBuilderService.updated.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((searchRequest) => {
|
||||
if (searchRequest) {
|
||||
this.hasValidQuery = true;
|
||||
this.prepareDialogForNewSearch(searchRequest);
|
||||
this.queryBuilderService.execute(false, searchRequest);
|
||||
} else {
|
||||
this.hasValidQuery = false;
|
||||
this.resetFolderToShow();
|
||||
this.clearSearch();
|
||||
}
|
||||
});
|
||||
|
||||
this.queryBuilderService.executed.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((results: NodePaging) => {
|
||||
if (this.hasValidQuery) {
|
||||
this.showSearchResults(results);
|
||||
}
|
||||
this.showSearchResults(results);
|
||||
});
|
||||
|
||||
this.userPreferencesService
|
||||
@@ -451,7 +442,12 @@ export class ContentNodeSelectorPanelComponent implements OnInit {
|
||||
this.siteId = chosenSite.entry.guid;
|
||||
this.setTitleIfCustomSite(chosenSite);
|
||||
this.siteChange.emit(chosenSite.entry.title);
|
||||
this.queryBuilderService.update();
|
||||
if (this.searchTerm) {
|
||||
this.executeSearch(this.searchTerm);
|
||||
} else {
|
||||
this.resetFolderToShow();
|
||||
this.clearSearch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -473,18 +469,11 @@ export class ContentNodeSelectorPanelComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* Prepares the dialog for a new search
|
||||
*
|
||||
* @param searchRequest request options
|
||||
*/
|
||||
prepareDialogForNewSearch(searchRequest: SearchRequest): void {
|
||||
this.target = searchRequest ? null : this.documentList;
|
||||
if (this.target) {
|
||||
this.infinitePaginationComponent.reset();
|
||||
}
|
||||
prepareDialogForNewSearch(): void {
|
||||
this.folderIdToShow = null;
|
||||
this.preselectedNodes = [];
|
||||
this.loadingSearchResults = true;
|
||||
this.addCorrespondingNodeIdsQuery();
|
||||
this.resetChosenNode();
|
||||
}
|
||||
|
||||
@@ -494,7 +483,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit {
|
||||
clear(): void {
|
||||
this.searchTerm = '';
|
||||
this.queryBuilderService.userQuery = '';
|
||||
this.queryBuilderService.update();
|
||||
this.executeSearch(this.searchTerm);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -516,25 +505,35 @@ export class ContentNodeSelectorPanelComponent implements OnInit {
|
||||
this.showingSearch.emit(this.showingSearchResults);
|
||||
}
|
||||
|
||||
private addCorrespondingNodeIdsQuery() {
|
||||
let extraParentFiltering = '';
|
||||
|
||||
private addCorrespondingNodeIdsQuery(): Observable<void> {
|
||||
if (this.customResourcesService.hasCorrespondingNodeIds(this.siteId)) {
|
||||
this.customResourcesService.getCorrespondingNodeIds(this.siteId).subscribe((nodeIds) => {
|
||||
if (nodeIds?.length) {
|
||||
nodeIds
|
||||
.filter((id) => id !== this.siteId)
|
||||
.forEach((extraId) => {
|
||||
extraParentFiltering += ` OR ANCESTOR:'workspace://SpacesStore/${extraId}'`;
|
||||
});
|
||||
}
|
||||
const parentFiltering = this.siteId ? `ANCESTOR:'workspace://SpacesStore/${this.siteId}'${extraParentFiltering}` : '';
|
||||
this.queryBuilderService.addFilterQuery(parentFiltering);
|
||||
});
|
||||
} else {
|
||||
const parentFiltering = this.siteId ? `ANCESTOR:'workspace://SpacesStore/${this.siteId}'` : '';
|
||||
this.queryBuilderService.addFilterQuery(parentFiltering);
|
||||
return this.customResourcesService.getCorrespondingNodeIds(this.siteId).pipe(
|
||||
map((nodeIds) => {
|
||||
let extraParentFiltering = '';
|
||||
if (nodeIds?.length) {
|
||||
nodeIds
|
||||
.filter((id) => id !== this.siteId)
|
||||
.forEach((extraId) => {
|
||||
extraParentFiltering += ` OR ANCESTOR:'workspace://SpacesStore/${extraId}'`;
|
||||
});
|
||||
}
|
||||
const parentFiltering = this.siteId ? `ANCESTOR:'workspace://SpacesStore/${this.siteId}'${extraParentFiltering}` : '';
|
||||
this.setParentFilterQuery(parentFiltering);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const siteFilter = this.siteId ? `ANCESTOR:'workspace://SpacesStore/${this.siteId}'` : '';
|
||||
this.setParentFilterQuery(siteFilter);
|
||||
return of(undefined);
|
||||
}
|
||||
|
||||
private setParentFilterQuery(parentFiltering: string) {
|
||||
if (this.lastParentFilterQuery) {
|
||||
this.queryBuilderService.removeFilterQuery(this.lastParentFilterQuery);
|
||||
}
|
||||
this.lastParentFilterQuery = parentFiltering || null;
|
||||
this.queryBuilderService.addFilterQuery(parentFiltering);
|
||||
}
|
||||
|
||||
private setSearchScopeToNodes() {
|
||||
@@ -619,7 +618,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit {
|
||||
this.queryBuilderService.paging.skipCount = pagination.skipCount;
|
||||
|
||||
if (this.searchTerm.length > 0) {
|
||||
this.queryBuilderService.update();
|
||||
this.executeSearch(this.searchTerm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,4 +695,14 @@ export class ContentNodeSelectorPanelComponent implements OnInit {
|
||||
skipCount: this.DEFAULT_PAGINATION.skipCount
|
||||
};
|
||||
}
|
||||
|
||||
private executeSearch(searchValue: string): void {
|
||||
this.prepareDialogForNewSearch();
|
||||
this.queryBuilderService.searchMode = 'formula';
|
||||
const wildcardSuffix = this.queryBuilderService.wildcardsEnabled ? '*' : '';
|
||||
this.queryBuilderService.userQuery = searchValue.length > 0 ? `(${searchValue}${wildcardSuffix})` : searchValue;
|
||||
this.addCorrespondingNodeIdsQuery()
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe(() => this.queryBuilderService.execute(false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,14 +34,12 @@ import { VersionCompatibilityService } from './version-compatibility/version-com
|
||||
import { contentAuthLoaderFactory } from './auth-loader/content-auth-loader-factory';
|
||||
import { ContentAuthLoaderService } from './auth-loader/content-auth-loader.service';
|
||||
import { CONTENT_UPLOAD_DIRECTIVES } from './upload';
|
||||
import { MaterialModule } from './material.module';
|
||||
import { AlfrescoApiService } from './services/alfresco-api.service';
|
||||
import { AlfrescoApiNoAuthService } from './api-factories/alfresco-api-no-auth.service';
|
||||
import { AlfrescoApiLoaderService, createAlfrescoApiInstance } from './api-factories/alfresco-api-v2-loader.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
MaterialModule,
|
||||
MatDatetimepickerModule,
|
||||
MatNativeDatetimeModule,
|
||||
...CONTENT_TAG_DIRECTIVES,
|
||||
@@ -59,7 +57,6 @@ import { AlfrescoApiLoaderService, createAlfrescoApiInstance } from './api-facto
|
||||
],
|
||||
providers: [provideTranslations('adf-content-services', 'assets/adf-content-services')],
|
||||
exports: [
|
||||
MaterialModule,
|
||||
...CONTENT_TAG_DIRECTIVES,
|
||||
...DOCUMENT_LIST_DIRECTIVES,
|
||||
...CONTENT_UPLOAD_DIRECTIVES,
|
||||
|
||||
@@ -14,16 +14,12 @@
|
||||
[formControlName]="'name'"
|
||||
adf-auto-focus
|
||||
/>
|
||||
|
||||
<mat-hint *ngIf="form.controls['name'].dirty">
|
||||
<span *ngIf="form.controls['name'].errors?.required">
|
||||
{{ 'CORE.FOLDER_DIALOG.FOLDER_NAME.ERRORS.REQUIRED' | translate }}
|
||||
</span>
|
||||
|
||||
<span *ngIf="!form.controls['name'].errors?.required && form.controls['name'].errors?.message">
|
||||
{{ form.controls['name'].errors?.message | translate }}
|
||||
</span>
|
||||
</mat-hint>
|
||||
@if (form.controls['name'].dirty && form.controls['name'].invalid) {
|
||||
<mat-error>
|
||||
<span class="adf-error-text"
|
||||
>@if (form.controls['name'].errors?.required) {{{ 'CORE.FOLDER_DIALOG.FOLDER_NAME.ERRORS.REQUIRED' | translate }}} @else if (form.controls['name'].errors?.message) {{{ form.controls['name'].errors?.message | translate }}}</span>
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="adf-full-width adf-folder-dialog-form-field">
|
||||
|
||||
@@ -175,6 +175,16 @@ describe('DocumentList', () => {
|
||||
expect(documentList.resetSelection).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call reloadWithoutResettingSelection and NOT resetSelection when reloadSilently$ is emitted', () => {
|
||||
spyOn(documentList, 'reloadWithoutResettingSelection').and.callThrough();
|
||||
spyOn(documentList, 'resetSelection').and.callThrough();
|
||||
|
||||
documentListService.reloadSilently();
|
||||
|
||||
expect(documentList.reloadWithoutResettingSelection).toHaveBeenCalled();
|
||||
expect(documentList.resetSelection).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('presets', () => {
|
||||
const validatePreset = (keys: string[]) => {
|
||||
const columns = documentList.data.getColumns();
|
||||
|
||||
@@ -561,6 +561,10 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On
|
||||
this.reload();
|
||||
});
|
||||
|
||||
this.documentListService.reloadSilently$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
||||
this.reloadWithoutResettingSelection();
|
||||
});
|
||||
|
||||
this.documentListService.resetSelection$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
||||
this.resetSelection();
|
||||
});
|
||||
|
||||
+35
-3
@@ -23,6 +23,8 @@ import { SimpleChange } from '@angular/core';
|
||||
import { SearchHeaderQueryBuilderService } from './../../../search/services/search-header-query-builder.service';
|
||||
import { FilterHeaderComponent } from './filter-header.component';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { SearchCategory } from '@alfresco/adf-content-services';
|
||||
import { NodePaging } from '@alfresco/js-api';
|
||||
|
||||
describe('FilterHeaderComponent', () => {
|
||||
let fixture: ComponentFixture<FilterHeaderComponent>;
|
||||
@@ -150,7 +152,7 @@ describe('FilterHeaderComponent', () => {
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(Object.keys(queryBuilder.filterRawParams).length).toBe(0);
|
||||
expect(queryBuilder.filterRawParams['name']).toBeUndefined();
|
||||
|
||||
component.value = { name: 'pinocchio' };
|
||||
const currentFolderNodeIdChange = new SimpleChange('current-node-id', 'next-node-id', true);
|
||||
@@ -158,11 +160,41 @@ describe('FilterHeaderComponent', () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(Object.keys(queryBuilder.filterRawParams).length).toBe(1);
|
||||
expect(queryBuilder.filterRawParams['name']).toBe('pinocchio');
|
||||
expect(queryBuilder.queryFragments['name']).toBe('pinocchio');
|
||||
});
|
||||
|
||||
it('should build a wildcard field query fragment for the queryName filter when wildcards are enabled', async () => {
|
||||
spyOn(queryBuilder, 'setCurrentRootFolderId');
|
||||
spyOn(queryBuilder, 'isCustomSourceNode').and.returnValue(false);
|
||||
spyOnProperty(queryBuilder, 'wildcardsEnabled', 'get').and.returnValue(true);
|
||||
queryBuilder.categories = [{ id: 'queryName', component: { settings: { field: 'cm:name' } } } as SearchCategory];
|
||||
|
||||
component.value = { queryName: 'pinocchio' };
|
||||
const currentFolderNodeIdChange = new SimpleChange('current-node-id', 'next-node-id', true);
|
||||
component.ngOnChanges({ currentFolderId: currentFolderNodeIdChange });
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(queryBuilder.filterRawParams['queryName']).toBe('pinocchio');
|
||||
expect(queryBuilder.queryFragments['queryName']).toBe(`cm:name:'*pinocchio*'`);
|
||||
});
|
||||
|
||||
it('should build a non-wildcard field query fragment for the queryName filter when wildcards are disabled', async () => {
|
||||
spyOn(queryBuilder, 'setCurrentRootFolderId');
|
||||
spyOn(queryBuilder, 'isCustomSourceNode').and.returnValue(false);
|
||||
spyOnProperty(queryBuilder, 'wildcardsEnabled', 'get').and.returnValue(false);
|
||||
queryBuilder.categories = [{ id: 'queryName', component: { settings: { field: 'cm:name' } } } as SearchCategory];
|
||||
|
||||
component.value = { queryName: 'pinocchio' };
|
||||
const currentFolderNodeIdChange = new SimpleChange('current-node-id', 'next-node-id', true);
|
||||
component.ngOnChanges({ currentFolderId: currentFolderNodeIdChange });
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(queryBuilder.queryFragments['queryName']).toBe(`cm:name:'pinocchio'`);
|
||||
});
|
||||
|
||||
it('should emit filterSelection when a filter is changed', (done) => {
|
||||
spyOn(queryBuilder, 'getActiveFilters').and.returnValue([{ key: 'name', value: 'pinocchio' }]);
|
||||
|
||||
@@ -193,7 +225,7 @@ describe('FilterHeaderComponent', () => {
|
||||
it('should emit searchResultsReady when search query builder executes', (done) => {
|
||||
fixture.detectChanges(); // Initialize component (triggers ngOnInit)
|
||||
|
||||
const mockNodePaging: any = { list: { entries: [] } };
|
||||
const mockNodePaging: NodePaging = { list: { entries: [] } };
|
||||
|
||||
component.searchResultsReady.subscribe((nodePaging) => {
|
||||
expect(nodePaging).toBe(mockNodePaging);
|
||||
|
||||
+10
-3
@@ -115,9 +115,16 @@ export class FilterHeaderComponent implements OnInit, OnChanges {
|
||||
|
||||
const operator = this.searchFilterQueryBuilder.getOperatorForFilterId(key) || 'OR';
|
||||
this.searchFilterQueryBuilder.filterRawParams[key] = this.value[key];
|
||||
this.searchFilterQueryBuilder.queryFragments[key] = Array.isArray(this.value[key])
|
||||
? this.value[key].join(` ${operator} `)
|
||||
: this.value[key];
|
||||
if (key === 'queryName') {
|
||||
const filterConfig = this.searchFilterQueryBuilder.categories.find((category) => category.id === key);
|
||||
const wildcardSuffix = this.searchFilterQueryBuilder.wildcardsEnabled ? '*' : '';
|
||||
this.searchFilterQueryBuilder.queryFragments[key] =
|
||||
`${filterConfig.component.settings.field}:'${wildcardSuffix}${this.value[key]}${wildcardSuffix}'`;
|
||||
} else {
|
||||
this.searchFilterQueryBuilder.queryFragments[key] = Array.isArray(this.value[key])
|
||||
? this.value[key].join(` ${operator} `)
|
||||
: this.value[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
this.searchFilterQueryBuilder.setCurrentRootFolderId(currentFolderId);
|
||||
|
||||
+54
-1
@@ -24,6 +24,9 @@ import {
|
||||
FavoritePagingList,
|
||||
NodeEntry,
|
||||
NodePaging,
|
||||
Person,
|
||||
PersonEntry,
|
||||
ResultSetPaging,
|
||||
Site,
|
||||
SiteEntry,
|
||||
SiteMember,
|
||||
@@ -194,7 +197,57 @@ describe('CustomResourcesService', () => {
|
||||
spyOn(customResourcesService, 'getRecentFiles').and.stub();
|
||||
|
||||
customResourcesService.loadFolderByNodeId('-recent-', pagination, ['include'], 'where', ['filters']);
|
||||
expect(customResourcesService.getRecentFiles).toHaveBeenCalledWith('-me-', pagination, ['filters']);
|
||||
expect(customResourcesService.getRecentFiles).toHaveBeenCalledWith('-me-', pagination, ['filters'], ['include']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRecentFiles', () => {
|
||||
const pagination: PaginationModel = { maxItems: 100, skipCount: 0 };
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(customResourcesService.peopleApi, 'getPerson').and.returnValue(
|
||||
Promise.resolve(new PersonEntry({ entry: new Person({ id: 'user' }) }))
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass includeFields through to the search request, keeping the defaults', (done) => {
|
||||
const searchSpy = spyOn(customResourcesService.searchApi, 'search').and.returnValue(Promise.resolve(new ResultSetPaging()));
|
||||
|
||||
customResourcesService.getRecentFiles('-me-', pagination, undefined, ['isFavorite']).subscribe(() => {
|
||||
expect(searchSpy).toHaveBeenCalledTimes(1);
|
||||
expect(searchSpy.calls.mostRecent().args[0].include).toEqual([
|
||||
'path',
|
||||
'properties',
|
||||
'allowableOperations',
|
||||
'aspectNames',
|
||||
'isFavorite'
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should keep the default include fields when no includeFields are provided', (done) => {
|
||||
const searchSpy = spyOn(customResourcesService.searchApi, 'search').and.returnValue(Promise.resolve(new ResultSetPaging()));
|
||||
|
||||
customResourcesService.getRecentFiles('-me-', pagination).subscribe(() => {
|
||||
expect(searchSpy.calls.mostRecent().args[0].include).toEqual(['path', 'properties', 'allowableOperations', 'aspectNames']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not duplicate an includeField that is already a default', (done) => {
|
||||
const searchSpy = spyOn(customResourcesService.searchApi, 'search').and.returnValue(Promise.resolve(new ResultSetPaging()));
|
||||
|
||||
customResourcesService.getRecentFiles('-me-', pagination, undefined, ['properties', 'isFavorite']).subscribe(() => {
|
||||
expect(searchSpy.calls.mostRecent().args[0].include).toEqual([
|
||||
'path',
|
||||
'properties',
|
||||
'allowableOperations',
|
||||
'aspectNames',
|
||||
'isFavorite'
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -73,9 +73,10 @@ export class CustomResourcesService {
|
||||
* @param personId ID of the user
|
||||
* @param pagination Specifies how to paginate the results
|
||||
* @param filters Specifies additional filters to apply (joined with **AND**)
|
||||
* @param includeFields List of data field names to include in the results
|
||||
* @returns List of nodes for the recently used files
|
||||
*/
|
||||
getRecentFiles(personId: string, pagination: PaginationModel, filters?: string[]): Observable<ResultSetPaging> {
|
||||
getRecentFiles(personId: string, pagination: PaginationModel, filters?: string[], includeFields: string[] = []): Observable<ResultSetPaging> {
|
||||
const defaultFilter = [
|
||||
'TYPE:"content"',
|
||||
'-PATH:"//cm:wiki/*"',
|
||||
@@ -121,7 +122,7 @@ export class CustomResourcesService {
|
||||
language: SEARCH_LANGUAGE.AFTS
|
||||
},
|
||||
filterQueries,
|
||||
include: ['path', 'properties', 'allowableOperations', 'aspectNames'],
|
||||
include: [...new Set(['path', 'properties', 'allowableOperations', 'aspectNames', ...includeFields])],
|
||||
sort: [
|
||||
{
|
||||
type: 'FIELD',
|
||||
@@ -380,7 +381,7 @@ export class CustomResourcesService {
|
||||
} else if (nodeId === '-favorites-') {
|
||||
return this.loadFavorites(pagination, includeFields, where);
|
||||
} else if (nodeId === '-recent-') {
|
||||
return this.getRecentFiles('-me-', pagination, filters);
|
||||
return this.getRecentFiles('-me-', pagination, filters, includeFields);
|
||||
} else {
|
||||
return of(null);
|
||||
}
|
||||
|
||||
@@ -93,6 +93,13 @@ describe('DocumentListService', () => {
|
||||
service.reload();
|
||||
});
|
||||
|
||||
it('should emit reloadSilently$ when reloadSilently is called', (done) => {
|
||||
service.reloadSilently$.subscribe(() => {
|
||||
done();
|
||||
});
|
||||
service.reloadSilently();
|
||||
});
|
||||
|
||||
it('should return the folder info', fakeAsync(() => {
|
||||
spyOn(service.nodes, 'listNodeChildren').and.returnValue(Promise.resolve(fakeFolder as any));
|
||||
|
||||
|
||||
@@ -41,10 +41,14 @@ export class DocumentListService implements DocumentListLoader {
|
||||
|
||||
private readonly _reload = new Subject<void>();
|
||||
private readonly _resetSelection = new Subject<void>();
|
||||
private readonly _reloadSilently = new Subject<void>();
|
||||
|
||||
/** Gets an observable that emits when the document list should be reloaded. */
|
||||
reload$ = this._reload.asObservable();
|
||||
|
||||
/** Gets an observable that emits when the document list should be reloaded without resetting the current selection. */
|
||||
reloadSilently$ = this._reloadSilently.asObservable();
|
||||
|
||||
/** Gets an observable that emits when the selection should be reset. */
|
||||
resetSelection$ = this._resetSelection.asObservable();
|
||||
|
||||
@@ -53,6 +57,11 @@ export class DocumentListService implements DocumentListLoader {
|
||||
this._reload.next();
|
||||
}
|
||||
|
||||
/** Reloads the document list without resetting the current selection. */
|
||||
reloadSilently() {
|
||||
this._reloadSilently.next();
|
||||
}
|
||||
|
||||
/** Resets the selection. */
|
||||
resetSelection() {
|
||||
this._resetSelection.next();
|
||||
|
||||
@@ -627,6 +627,10 @@
|
||||
"LOAD-MORE-BUTTON": "Mehr laden {{ name }}",
|
||||
"ACTIONS": {
|
||||
"TOOLTIP": "Aktionsmenü öffnen"
|
||||
},
|
||||
"ARIA": {
|
||||
"SELECTED": "{{ name }} ausgewählt",
|
||||
"DESELECTED": "{{name}} Auswahl aufgehoben"
|
||||
}
|
||||
},
|
||||
"LIBRARY": {
|
||||
@@ -725,14 +729,6 @@
|
||||
"JOIN_REQUESTED": "Anfrage zum Bibliotheksbeitritt abgeschickt"
|
||||
}
|
||||
},
|
||||
"KNOWLEDGE_RETRIEVAL": {
|
||||
"SEARCH": {
|
||||
"WARNINGS": {
|
||||
"TOO_MANY_FILES_SELECTED": "Bitte wählen Sie nicht mehr als {{ maxFiles }} Dateien.",
|
||||
"FOLDER_SELECTED": "Ordner sind nicht kompatibel mit KI Agents."
|
||||
}
|
||||
}
|
||||
},
|
||||
"NODE_FAVORITE_DIRECTIVE": {
|
||||
"MESSAGES": {
|
||||
"NODE_ADDED": "{{ name }} zu Favoriten hinzugefügt",
|
||||
|
||||
@@ -627,6 +627,10 @@
|
||||
"LOAD-MORE-BUTTON": "Load more {{ name }}",
|
||||
"ACTIONS": {
|
||||
"TOOLTIP": "Open actions menu"
|
||||
},
|
||||
"ARIA": {
|
||||
"SELECTED": "{{ name }} selected",
|
||||
"DESELECTED": "{{ name }} deselected"
|
||||
}
|
||||
},
|
||||
"LIBRARY": {
|
||||
@@ -725,14 +729,6 @@
|
||||
"JOIN_REQUESTED": "Request sent to join this library"
|
||||
}
|
||||
},
|
||||
"KNOWLEDGE_RETRIEVAL": {
|
||||
"SEARCH": {
|
||||
"WARNINGS": {
|
||||
"TOO_MANY_FILES_SELECTED": "Please select no more than {{ maxFiles }} files.",
|
||||
"FOLDER_SELECTED": "Folders are not compatible with AI Agents."
|
||||
}
|
||||
}
|
||||
},
|
||||
"NODE_FAVORITE_DIRECTIVE": {
|
||||
"MESSAGES": {
|
||||
"NODE_ADDED": "Added {{ name }} to favorites",
|
||||
|
||||
@@ -627,6 +627,10 @@
|
||||
"LOAD-MORE-BUTTON": "Load more {{ name }}",
|
||||
"ACTIONS": {
|
||||
"TOOLTIP": "Open actions menu"
|
||||
},
|
||||
"ARIA": {
|
||||
"SELECTED": "{{ name }} selected",
|
||||
"DESELECTED": "{{ name }} deselected"
|
||||
}
|
||||
},
|
||||
"LIBRARY": {
|
||||
@@ -725,14 +729,6 @@
|
||||
"JOIN_REQUESTED": "Request sent to join this library"
|
||||
}
|
||||
},
|
||||
"KNOWLEDGE_RETRIEVAL": {
|
||||
"SEARCH": {
|
||||
"WARNINGS": {
|
||||
"TOO_MANY_FILES_SELECTED": "Please select no more than {{ maxFiles }} files.",
|
||||
"FOLDER_SELECTED": "Folders are not compatible with AI Agents."
|
||||
}
|
||||
}
|
||||
},
|
||||
"NODE_FAVORITE_DIRECTIVE": {
|
||||
"MESSAGES": {
|
||||
"NODE_ADDED": "Added {{ name }} to favorites",
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
"ATTACH": "Adjuntar",
|
||||
"CHOOSE_ITEM": "Seleccione el contenido que adjuntar de '{{ name }}'",
|
||||
"COPY": "Copiar",
|
||||
"LINK": "Create Link",
|
||||
"LINK": "Crear enlace",
|
||||
"COPY_ITEM": "Copiar '{{ name }}' a...",
|
||||
"LOCATION": "Ubicación",
|
||||
"MOVE": "Mover",
|
||||
@@ -627,6 +627,10 @@
|
||||
"LOAD-MORE-BUTTON": "Cargar más {{ name }}",
|
||||
"ACTIONS": {
|
||||
"TOOLTIP": "Abrir menú de acciones"
|
||||
},
|
||||
"ARIA": {
|
||||
"SELECTED": "{{name}} seleccionado",
|
||||
"DESELECTED": "{{name}} deseleccionado"
|
||||
}
|
||||
},
|
||||
"LIBRARY": {
|
||||
@@ -725,14 +729,6 @@
|
||||
"JOIN_REQUESTED": "Se ha enviado una solicitud para unirse a esta biblioteca"
|
||||
}
|
||||
},
|
||||
"KNOWLEDGE_RETRIEVAL": {
|
||||
"SEARCH": {
|
||||
"WARNINGS": {
|
||||
"TOO_MANY_FILES_SELECTED": "No seleccione más de {{ maxFiles }} ficheros.",
|
||||
"FOLDER_SELECTED": "Las carpetas no son compatibles con los Agentes IA."
|
||||
}
|
||||
}
|
||||
},
|
||||
"NODE_FAVORITE_DIRECTIVE": {
|
||||
"MESSAGES": {
|
||||
"NODE_ADDED": "Añadido {{ name }} a favoritos",
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
"ATTACH": "Joindre",
|
||||
"CHOOSE_ITEM": "Sélectionner le contenu à joindre à partir de '{{ name }}'",
|
||||
"COPY": "Copier",
|
||||
"LINK": "Create Link",
|
||||
"LINK": "Créer un lien",
|
||||
"COPY_ITEM": "Copier '{{ name }}' dans...",
|
||||
"LOCATION": "Emplacement",
|
||||
"MOVE": "Déplacer",
|
||||
@@ -627,6 +627,10 @@
|
||||
"LOAD-MORE-BUTTON": "Charger plus de {{ name }}",
|
||||
"ACTIONS": {
|
||||
"TOOLTIP": "Ouvrir le menu actions"
|
||||
},
|
||||
"ARIA": {
|
||||
"SELECTED": "{{name}} sélectionné",
|
||||
"DESELECTED": "{{name}} désélectionné"
|
||||
}
|
||||
},
|
||||
"LIBRARY": {
|
||||
@@ -725,14 +729,6 @@
|
||||
"JOIN_REQUESTED": "Demande envoyée pour rejoindre cette bibliothèque"
|
||||
}
|
||||
},
|
||||
"KNOWLEDGE_RETRIEVAL": {
|
||||
"SEARCH": {
|
||||
"WARNINGS": {
|
||||
"TOO_MANY_FILES_SELECTED": "Veuillez ne pas sélectionner plus de {{ maxFiles }} fichiers.",
|
||||
"FOLDER_SELECTED": "Les dossiers ne sont pas compatibles avec les agents IA."
|
||||
}
|
||||
}
|
||||
},
|
||||
"NODE_FAVORITE_DIRECTIVE": {
|
||||
"MESSAGES": {
|
||||
"NODE_ADDED": "Ajout de {{ name }} aux favoris",
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
"ATTACH": "Allega",
|
||||
"CHOOSE_ITEM": "Selezionare il contenuto da allegare da \"{{ name }}\"",
|
||||
"COPY": "Copia",
|
||||
"LINK": "Create Link",
|
||||
"LINK": "Crea collegamento",
|
||||
"COPY_ITEM": "Copia '{{ name }}' in...",
|
||||
"LOCATION": "Posizione",
|
||||
"MOVE": "Sposta",
|
||||
@@ -627,6 +627,10 @@
|
||||
"LOAD-MORE-BUTTON": "Carica altri {{ name }}",
|
||||
"ACTIONS": {
|
||||
"TOOLTIP": "Apri menu azioni"
|
||||
},
|
||||
"ARIA": {
|
||||
"SELECTED": "{{name}} selezionato",
|
||||
"DESELECTED": "{{name}} deselezionato"
|
||||
}
|
||||
},
|
||||
"LIBRARY": {
|
||||
@@ -725,14 +729,6 @@
|
||||
"JOIN_REQUESTED": "Richiesta di partecipazione alla libreria inviata"
|
||||
}
|
||||
},
|
||||
"KNOWLEDGE_RETRIEVAL": {
|
||||
"SEARCH": {
|
||||
"WARNINGS": {
|
||||
"TOO_MANY_FILES_SELECTED": "Selezionare più di {{ maxFiles }} file.",
|
||||
"FOLDER_SELECTED": "Le cartelle non sono compatibili con gli agenti IA."
|
||||
}
|
||||
}
|
||||
},
|
||||
"NODE_FAVORITE_DIRECTIVE": {
|
||||
"MESSAGES": {
|
||||
"NODE_ADDED": "Aggiunto {{ name }} ai preferiti",
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
"ATTACH": "Załącz",
|
||||
"CHOOSE_ITEM": "Wybierz treść do dołączenia z „{{ name }}”",
|
||||
"COPY": "Kopiuj",
|
||||
"LINK": "Create Link",
|
||||
"LINK": "Utwórz link",
|
||||
"COPY_ITEM": "Kopiuj '{{ name }}' do...",
|
||||
"LOCATION": "Lokalizacja",
|
||||
"MOVE": "Przenieś",
|
||||
@@ -627,6 +627,10 @@
|
||||
"LOAD-MORE-BUTTON": "Wczytaj {{ name }}",
|
||||
"ACTIONS": {
|
||||
"TOOLTIP": "Otwórz menu akcji"
|
||||
},
|
||||
"ARIA": {
|
||||
"SELECTED": "Wybrano {{name}}",
|
||||
"DESELECTED": "Odznaczono {{name}}"
|
||||
}
|
||||
},
|
||||
"LIBRARY": {
|
||||
@@ -725,14 +729,6 @@
|
||||
"JOIN_REQUESTED": "Żądanie dołączenia do tej biblioteki zostało wysłane"
|
||||
}
|
||||
},
|
||||
"KNOWLEDGE_RETRIEVAL": {
|
||||
"SEARCH": {
|
||||
"WARNINGS": {
|
||||
"TOO_MANY_FILES_SELECTED": "Proszę wybrać nie więcej niż {{ maxFiles }} plików.",
|
||||
"FOLDER_SELECTED": "Foldery nie są kompatybilne z agentami AI."
|
||||
}
|
||||
}
|
||||
},
|
||||
"NODE_FAVORITE_DIRECTIVE": {
|
||||
"MESSAGES": {
|
||||
"NODE_ADDED": "Dodano {{ name }} do ulubionych",
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
"ATTACH": "Anexar",
|
||||
"CHOOSE_ITEM": "Selecionar o conteúdo a anexar '{{ name }}'",
|
||||
"COPY": "Copiar",
|
||||
"LINK": "Create Link",
|
||||
"LINK": "Criar link",
|
||||
"COPY_ITEM": "Copiar '{{ name }}' para...",
|
||||
"LOCATION": "Localização",
|
||||
"MOVE": "Mover",
|
||||
@@ -627,6 +627,10 @@
|
||||
"LOAD-MORE-BUTTON": "Carregar mais {{ name }}",
|
||||
"ACTIONS": {
|
||||
"TOOLTIP": "Abrir menu de ações"
|
||||
},
|
||||
"ARIA": {
|
||||
"SELECTED": "{{name}} selecionado",
|
||||
"DESELECTED": "{{name}} desmarcado"
|
||||
}
|
||||
},
|
||||
"LIBRARY": {
|
||||
@@ -725,14 +729,6 @@
|
||||
"JOIN_REQUESTED": "Solicitação enviada para aderir a esta biblioteca"
|
||||
}
|
||||
},
|
||||
"KNOWLEDGE_RETRIEVAL": {
|
||||
"SEARCH": {
|
||||
"WARNINGS": {
|
||||
"TOO_MANY_FILES_SELECTED": "Selecione no máximo {{ maxFiles }} ficheiros.",
|
||||
"FOLDER_SELECTED": "As pastas não são compatíveis com os Agentes de IA."
|
||||
}
|
||||
}
|
||||
},
|
||||
"NODE_FAVORITE_DIRECTIVE": {
|
||||
"MESSAGES": {
|
||||
"NODE_ADDED": "Adicionado {{ name }} aos favoritos",
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
/*!
|
||||
* @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 { NgModule } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { MatRippleModule, MatOptionModule } from '@angular/material/core';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatSliderModule } from '@angular/material/slider';
|
||||
import { MatTreeModule } from '@angular/material/tree';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
|
||||
/** @deprecated this module is deprecated and will be removed in future versions */
|
||||
@NgModule({
|
||||
imports: [
|
||||
MatButtonModule,
|
||||
MatAutocompleteModule,
|
||||
MatChipsModule,
|
||||
MatDialogModule,
|
||||
MatIconModule,
|
||||
MatCardModule,
|
||||
MatInputModule,
|
||||
MatListModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatProgressBarModule,
|
||||
MatRippleModule,
|
||||
MatMenuModule,
|
||||
MatOptionModule,
|
||||
MatExpansionModule,
|
||||
MatSelectModule,
|
||||
MatCheckboxModule,
|
||||
MatDatepickerModule,
|
||||
MatSlideToggleModule,
|
||||
MatRadioModule,
|
||||
MatSliderModule,
|
||||
MatTreeModule,
|
||||
MatBadgeModule
|
||||
],
|
||||
exports: [
|
||||
MatButtonModule,
|
||||
MatAutocompleteModule,
|
||||
MatChipsModule,
|
||||
MatDialogModule,
|
||||
MatIconModule,
|
||||
MatCardModule,
|
||||
MatInputModule,
|
||||
MatListModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatProgressBarModule,
|
||||
MatRippleModule,
|
||||
MatMenuModule,
|
||||
MatOptionModule,
|
||||
MatExpansionModule,
|
||||
MatSelectModule,
|
||||
MatCheckboxModule,
|
||||
MatDatepickerModule,
|
||||
MatSlideToggleModule,
|
||||
MatRadioModule,
|
||||
MatSliderModule,
|
||||
MatTreeModule,
|
||||
MatBadgeModule
|
||||
]
|
||||
})
|
||||
export class MaterialModule {}
|
||||
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* @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.
|
||||
*/
|
||||
|
||||
export * from './public-api';
|
||||
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* @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.
|
||||
*/
|
||||
|
||||
export * from './prediction.service';
|
||||
@@ -1,53 +0,0 @@
|
||||
/*!
|
||||
* @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 { PredictionService } from './prediction.service';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { Prediction, PredictionEntry, PredictionPaging, PredictionPagingList, ReviewStatus } from '@alfresco/js-api';
|
||||
|
||||
describe('PredictionService', () => {
|
||||
let service: PredictionService;
|
||||
|
||||
const mockPredictionPaging = (): PredictionPaging => {
|
||||
const prediction = new Prediction();
|
||||
prediction.id = 'test id';
|
||||
const predictionEntry = new PredictionEntry({ entry: prediction });
|
||||
const predictionPagingList = new PredictionPagingList({ entries: [predictionEntry] });
|
||||
return new PredictionPaging({ list: predictionPagingList });
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: []
|
||||
});
|
||||
service = TestBed.inject(PredictionService);
|
||||
});
|
||||
|
||||
it('should call getPredictions on PredictionsApi with nodeId', () => {
|
||||
spyOn(service.predictionsApi, 'getPredictions').and.returnValue(Promise.resolve(mockPredictionPaging()));
|
||||
|
||||
service.getPredictions('test id');
|
||||
expect(service.predictionsApi.getPredictions).toHaveBeenCalledWith('test id');
|
||||
});
|
||||
|
||||
it('should call reviewPrediction on PredictionsApi with predictionId and reviewStatus', () => {
|
||||
spyOn(service.predictionsApi, 'reviewPrediction').and.returnValue(Promise.resolve());
|
||||
|
||||
service.reviewPrediction('test id', ReviewStatus.CONFIRMED);
|
||||
expect(service.predictionsApi.reviewPrediction).toHaveBeenCalledWith('test id', ReviewStatus.CONFIRMED);
|
||||
});
|
||||
});
|
||||
@@ -1,50 +0,0 @@
|
||||
/*!
|
||||
* @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 { Injectable, inject } from '@angular/core';
|
||||
import { PredictionsApi, PredictionPaging, ReviewStatus, LazyApi } from '@alfresco/js-api';
|
||||
import { from, Observable } from 'rxjs';
|
||||
import { AlfrescoApiService } from '../../services/alfresco-api.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PredictionService {
|
||||
private readonly apiService = inject(AlfrescoApiService);
|
||||
|
||||
@LazyApi((self: PredictionService) => new PredictionsApi(self.apiService.getInstance()))
|
||||
declare readonly predictionsApi: PredictionsApi;
|
||||
|
||||
/**
|
||||
* Get predictions for a given node
|
||||
*
|
||||
* @param nodeId The identifier of node.
|
||||
* @returns Observable<PredictionPaging>
|
||||
*/
|
||||
getPredictions(nodeId: string): Observable<PredictionPaging> {
|
||||
return from(this.predictionsApi.getPredictions(nodeId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Review a prediction
|
||||
*
|
||||
* @param predictionId The identifier of prediction.
|
||||
* @param reviewStatus Review status to apply.
|
||||
* @returns Observable<void>
|
||||
*/
|
||||
reviewPrediction(predictionId: string, reviewStatus: ReviewStatus): Observable<void> {
|
||||
return from(this.predictionsApi.reviewPrediction(predictionId, reviewStatus));
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* @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.
|
||||
*/
|
||||
|
||||
export * from './public-api';
|
||||
@@ -1,22 +0,0 @@
|
||||
/*!
|
||||
* @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.
|
||||
*/
|
||||
|
||||
export interface SearchAiInputState {
|
||||
active: boolean;
|
||||
selectedAgentId?: string;
|
||||
searchTerm?: string;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*!
|
||||
* @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.
|
||||
*/
|
||||
|
||||
export * from './services/search-ai.service';
|
||||
export * from './models/search-ai-input-state';
|
||||
@@ -1,246 +0,0 @@
|
||||
/*!
|
||||
* @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 { TestBed } from '@angular/core/testing';
|
||||
import { AiAnswerEntry, KnowledgeRetrievalConfigEntry, Node, QuestionModel, QuestionRequest } from '@alfresco/js-api';
|
||||
import { SearchAiService } from './search-ai.service';
|
||||
import { SearchAiInputState } from '../models/search-ai-input-state';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
describe('SearchAiService', () => {
|
||||
let service: SearchAiService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: []
|
||||
});
|
||||
service = TestBed.inject(SearchAiService);
|
||||
});
|
||||
|
||||
describe('ask', () => {
|
||||
it('should load information about question', (done) => {
|
||||
const question: QuestionModel = {
|
||||
question: 'some question',
|
||||
questionId: 'some id',
|
||||
restrictionQuery: { nodesIds: ['nodeId1', 'nodeId2'] }
|
||||
};
|
||||
spyOn(service.searchAiApi, 'ask').and.returnValue(Promise.resolve(question));
|
||||
const questionRequest: QuestionRequest = {
|
||||
question: 'some question',
|
||||
nodeIds: ['nodeId1', 'nodeId2'],
|
||||
agentId: 'some id'
|
||||
};
|
||||
|
||||
service.ask(questionRequest).subscribe((questionResponse) => {
|
||||
expect(questionResponse).toBe(question);
|
||||
expect(service.searchAiApi.ask).toHaveBeenCalledWith([questionRequest]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAnswer', () => {
|
||||
it('should load information about question', (done) => {
|
||||
const questionId = 'some id';
|
||||
const answer: AiAnswerEntry = {
|
||||
entry: {
|
||||
answer: 'Some answer 1',
|
||||
complete: true,
|
||||
question: 'Some question',
|
||||
objectReferences: [
|
||||
{
|
||||
objectId: 'some id 1',
|
||||
references: [
|
||||
{
|
||||
referenceId: 'some reference id 1',
|
||||
rank: 1,
|
||||
rankScore: 0.005
|
||||
},
|
||||
{
|
||||
referenceId: 'some reference id 2',
|
||||
rank: 2,
|
||||
rankScore: 0.004
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
objectId: 'some id 2',
|
||||
references: [
|
||||
{
|
||||
referenceId: 'some reference id 3',
|
||||
rank: 1,
|
||||
rankScore: 0.005
|
||||
},
|
||||
{
|
||||
referenceId: 'some reference id 4',
|
||||
rank: 2,
|
||||
rankScore: 0.004
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
spyOn(service.searchAiApi, 'getAnswer').and.returnValue(Promise.resolve(answer));
|
||||
|
||||
service.getAnswer(questionId).subscribe((answerResponse) => {
|
||||
expect(answerResponse).toBe(answer);
|
||||
expect(service.searchAiApi.getAnswer).toHaveBeenCalledWith(questionId);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConfig', () => {
|
||||
it('should load knowledge retrieval configuration', (done) => {
|
||||
const config: KnowledgeRetrievalConfigEntry = {
|
||||
entry: {
|
||||
knowledgeRetrievalUrl: 'https://some-url'
|
||||
}
|
||||
};
|
||||
spyOn(service.searchAiApi, 'getConfig').and.returnValue(Promise.resolve(config));
|
||||
|
||||
service.getConfig().subscribe((configResponse) => {
|
||||
expect(configResponse).toBe(config);
|
||||
expect(service.searchAiApi.getConfig).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateSearchAiInputState', () => {
|
||||
it('should trigger toggleSearchAiInput$', () => {
|
||||
const state: SearchAiInputState = {
|
||||
active: true,
|
||||
selectedAgentId: 'some id'
|
||||
};
|
||||
service.updateSearchAiInputState(state);
|
||||
|
||||
service.toggleSearchAiInput$.subscribe((receivedState) => {
|
||||
expect(receivedState).toBe(state);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkSearchAvailability', () => {
|
||||
let translateService: TranslateService;
|
||||
|
||||
const tooManyFilesSelectedError = 'Please select no more than 100 files.';
|
||||
const folderSelectedError = 'Folders are not compatible with AI Agents.';
|
||||
|
||||
beforeEach(() => {
|
||||
translateService = TestBed.inject(TranslateService);
|
||||
spyOn(translateService, 'instant').and.callFake((key) => {
|
||||
switch (key) {
|
||||
case 'KNOWLEDGE_RETRIEVAL.SEARCH.WARNINGS.TOO_MANY_FILES_SELECTED':
|
||||
return tooManyFilesSelectedError;
|
||||
case 'KNOWLEDGE_RETRIEVAL.SEARCH.WARNINGS.FOLDER_SELECTED':
|
||||
return folderSelectedError;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should not return error if user did not select any files', () => {
|
||||
expect(
|
||||
service.checkSearchAvailability({
|
||||
count: 0,
|
||||
nodes: [],
|
||||
libraries: [],
|
||||
isEmpty: true
|
||||
})
|
||||
).toEqual('');
|
||||
});
|
||||
|
||||
it('should return error for too many files selected', () => {
|
||||
expect(
|
||||
service.checkSearchAvailability({
|
||||
count: 101,
|
||||
nodes: [],
|
||||
libraries: [],
|
||||
isEmpty: false
|
||||
})
|
||||
).toBe(tooManyFilesSelectedError);
|
||||
expect(translateService.instant).toHaveBeenCalledWith('KNOWLEDGE_RETRIEVAL.SEARCH.WARNINGS.TOO_MANY_FILES_SELECTED', {
|
||||
maxFiles: 100,
|
||||
key: 'KNOWLEDGE_RETRIEVAL.SEARCH.WARNINGS.TOO_MANY_FILES_SELECTED'
|
||||
});
|
||||
});
|
||||
|
||||
it('should return error for folder selected', () => {
|
||||
expect(
|
||||
service.checkSearchAvailability({
|
||||
count: 1,
|
||||
nodes: [
|
||||
{
|
||||
entry: {
|
||||
isFolder: true
|
||||
} as Node
|
||||
}
|
||||
],
|
||||
libraries: [],
|
||||
isEmpty: false
|
||||
})
|
||||
).toBe(folderSelectedError);
|
||||
});
|
||||
|
||||
it('should return error for folder and if non text mime type node is selected', () => {
|
||||
expect(
|
||||
service.checkSearchAvailability({
|
||||
count: 1,
|
||||
nodes: [
|
||||
{
|
||||
entry: {
|
||||
isFolder: true,
|
||||
content: {
|
||||
mimeType: 'some mime type',
|
||||
mimeTypeName: 'some mime type',
|
||||
sizeInBytes: 100
|
||||
}
|
||||
} as Node
|
||||
}
|
||||
],
|
||||
libraries: [],
|
||||
isEmpty: false
|
||||
})
|
||||
).toBe(folderSelectedError);
|
||||
});
|
||||
|
||||
it('should return more than one error if more validators detected issues', () => {
|
||||
expect(
|
||||
service.checkSearchAvailability({
|
||||
count: 101,
|
||||
nodes: [
|
||||
{
|
||||
entry: {
|
||||
isFolder: true,
|
||||
content: {
|
||||
mimeType: 'image/jpeg',
|
||||
mimeTypeName: 'image/jpeg',
|
||||
sizeInBytes: 100
|
||||
}
|
||||
} as Node
|
||||
}
|
||||
],
|
||||
libraries: [],
|
||||
isEmpty: false
|
||||
})
|
||||
).toBe(`${tooManyFilesSelectedError} ${folderSelectedError}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,105 +0,0 @@
|
||||
/*!
|
||||
* @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 { Injectable, inject } from '@angular/core';
|
||||
import { AiAnswerEntry, KnowledgeRetrievalConfigEntry, LazyApi, QuestionModel, QuestionRequest, SearchAiApi } from '@alfresco/js-api';
|
||||
import { BehaviorSubject, from, Observable } from 'rxjs';
|
||||
import { SelectionState } from '@alfresco/adf-extensions';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { SearchAiInputState } from '../models/search-ai-input-state';
|
||||
import { AlfrescoApiService } from '../../services';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SearchAiService {
|
||||
private readonly apiService = inject(AlfrescoApiService);
|
||||
private readonly translateService = inject(TranslateService);
|
||||
|
||||
private readonly toggleSearchAiInput = new BehaviorSubject<SearchAiInputState>({
|
||||
active: false
|
||||
});
|
||||
|
||||
@LazyApi((self: SearchAiService) => new SearchAiApi(self.apiService.getInstance()))
|
||||
declare readonly searchAiApi: SearchAiApi;
|
||||
|
||||
toggleSearchAiInput$ = this.toggleSearchAiInput.asObservable();
|
||||
|
||||
/**
|
||||
* Update the state of the search AI input.
|
||||
*
|
||||
* @param state The new state of the search AI input.
|
||||
*/
|
||||
updateSearchAiInputState(state: SearchAiInputState): void {
|
||||
this.toggleSearchAiInput.next(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask a question to the AI.
|
||||
*
|
||||
* @param question The question to ask.
|
||||
* @returns QuestionModel object containing information about questions.
|
||||
*/
|
||||
ask(question: QuestionRequest): Observable<QuestionModel> {
|
||||
return from(this.searchAiApi.ask([question]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an answer to specific question.
|
||||
*
|
||||
* @param questionId The ID of the question to get an answer for.
|
||||
* @returns AiAnswerEntry object containing the answer.
|
||||
*/
|
||||
getAnswer(questionId: string): Observable<AiAnswerEntry> {
|
||||
return from(this.searchAiApi.getAnswer(questionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the knowledge retrieval configuration.
|
||||
*
|
||||
* @returns KnowledgeRetrievalConfigEntry object containing the configuration.
|
||||
*/
|
||||
getConfig(): Observable<KnowledgeRetrievalConfigEntry> {
|
||||
return from(this.searchAiApi.getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if using of search is possible (if all conditions are met).
|
||||
*
|
||||
* @param selectedNodesState information about selected nodes.
|
||||
* @param maxSelectedNodes max number of selected nodes. Default 100.
|
||||
* @returns string with error if any condition is not met, empty string otherwise.
|
||||
*/
|
||||
checkSearchAvailability(selectedNodesState: SelectionState, maxSelectedNodes = 100): string {
|
||||
const messages: {
|
||||
key: string;
|
||||
[parameter: string]: number | string;
|
||||
}[] = [];
|
||||
if (selectedNodesState.count > maxSelectedNodes) {
|
||||
messages.push({
|
||||
key: 'KNOWLEDGE_RETRIEVAL.SEARCH.WARNINGS.TOO_MANY_FILES_SELECTED',
|
||||
maxFiles: maxSelectedNodes
|
||||
});
|
||||
}
|
||||
if (selectedNodesState.nodes.some((node) => node.entry.isFolder)) {
|
||||
messages.push({
|
||||
key: 'KNOWLEDGE_RETRIEVAL.SEARCH.WARNINGS.FOLDER_SELECTED'
|
||||
});
|
||||
}
|
||||
return messages.map((message) => this.translateService.instant(message.key, message)).join(' ');
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -44,7 +44,7 @@ describe('SearchCheckListComponent', () => {
|
||||
queryFragments: {},
|
||||
filterRawParams: {},
|
||||
populateFilters: new ReplaySubject(1),
|
||||
update: jasmine.createSpy()
|
||||
execute: jasmine.createSpy('execute')
|
||||
} as any;
|
||||
});
|
||||
|
||||
@@ -134,7 +134,7 @@ describe('SearchCheckListComponent', () => {
|
||||
|
||||
component.reset();
|
||||
|
||||
expect(component.context.update).toHaveBeenCalled();
|
||||
expect(component.context.execute).toHaveBeenCalled();
|
||||
expect(component.context.queryFragments[component.id]).toBe('');
|
||||
expect(component.context.filterRawParams[component.id]).toBeUndefined();
|
||||
});
|
||||
@@ -226,7 +226,7 @@ describe('SearchCheckListComponent', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.context.queryFragments[component.id]).toBe('');
|
||||
expect(component.context.update).not.toHaveBeenCalled();
|
||||
expect(component.context.execute).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should populate filter state when populate filters event has been observed', () => {
|
||||
|
||||
+3
-3
@@ -107,7 +107,7 @@ export class SearchCheckListComponent implements SearchWidget, OnInit {
|
||||
this.clearOptions();
|
||||
if (this.id && this.context && this.enableChangeUpdate) {
|
||||
this.updateDisplayValue();
|
||||
this.context.update();
|
||||
this.context.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ export class SearchCheckListComponent implements SearchWidget, OnInit {
|
||||
if (this.id && this.context) {
|
||||
this.updateDisplayValue();
|
||||
if (updateContext) {
|
||||
this.context.update();
|
||||
this.context.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,7 +178,7 @@ export class SearchCheckListComponent implements SearchWidget, OnInit {
|
||||
this.context.queryFragments[this.id] = query;
|
||||
this.updateDisplayValue();
|
||||
if (updateContext) {
|
||||
this.context.update();
|
||||
this.context.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -309,6 +309,13 @@ describe('SearchChipAutocompleteInputComponent', () => {
|
||||
expect(inputChangedSpy).toHaveBeenCalledOnceWith('test-value');
|
||||
});
|
||||
|
||||
it('should not emit input value when input is empty', async () => {
|
||||
const inputChangedSpy = spyOn(component.inputChanged, 'emit');
|
||||
enterNewInputValue('');
|
||||
await fixture.whenStable();
|
||||
expect(inputChangedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('isOptionSelected', () => {
|
||||
beforeEach(() => {
|
||||
component.autocompleteOptions = [{ value: 'option1' }, { value: 'option2' }];
|
||||
|
||||
+4
-2
@@ -108,8 +108,10 @@ export class SearchChipAutocompleteInputComponent implements OnInit, OnChanges {
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
)
|
||||
.subscribe((value: string) => {
|
||||
this.filteredOptions = this.filter(this.autocompleteOptions, value);
|
||||
this.inputChanged.emit(value);
|
||||
if (value) {
|
||||
this.filteredOptions = this.filter(this.autocompleteOptions, value);
|
||||
this.inputChanged.emit(value);
|
||||
}
|
||||
});
|
||||
this.onReset$?.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.reset());
|
||||
this.selectedOptions = this.preselectedOptions ?? [];
|
||||
|
||||
+1
-1
@@ -100,7 +100,7 @@ describe('SearchChipListComponent', () => {
|
||||
});
|
||||
|
||||
it('should remove the entry upon remove button click', async () => {
|
||||
spyOn(searchFacetFiltersService, 'unselectFacetBucket').and.callThrough();
|
||||
spyOn(searchFacetFiltersService, 'unselectFacetBucket').and.stub();
|
||||
|
||||
searchFacetFiltersService.selectedBuckets = [
|
||||
{
|
||||
|
||||
+4
-4
@@ -79,7 +79,7 @@ describe('SearchDateRangeTabbedComponent', () => {
|
||||
},
|
||||
filterRawParams: {},
|
||||
populateFilters: new ReplaySubject(1),
|
||||
update: jasmine.createSpy('update')
|
||||
execute: jasmine.createSpy('execute')
|
||||
} as any;
|
||||
component.settings = {
|
||||
hideDefaultAction: false,
|
||||
@@ -210,7 +210,7 @@ describe('SearchDateRangeTabbedComponent', () => {
|
||||
expect(component.context.filterRawParams[component.id].modifiedDate).toEqual(anyMockDate);
|
||||
});
|
||||
|
||||
it('should trigger context.update() when values are submitted', () => {
|
||||
it('should trigger context.execute() when values are submitted', () => {
|
||||
component.onDateRangedValueChanged(betweenMockData, 'createdDate');
|
||||
component.onDateRangedValueChanged(inLastMockData, 'modifiedDate');
|
||||
fixture.detectChanges();
|
||||
@@ -221,7 +221,7 @@ describe('SearchDateRangeTabbedComponent', () => {
|
||||
`createdDate:['${formatISO(startOfDay(betweenMockData.betweenStartDate))}' TO '${formatISO(endOfDay(betweenMockData.betweenEndDate))}']` +
|
||||
` AND modifiedDate:['${formatISO(startOfDay(inLastStartDate))}' TO '${formatISO(endOfToday())}']`;
|
||||
expect(component.context.queryFragments['dateRange']).toEqual(query);
|
||||
expect(component.context.update).toHaveBeenCalled();
|
||||
expect(component.context.execute).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should clear values and search filter when widget is reset', (done) => {
|
||||
@@ -231,7 +231,7 @@ describe('SearchDateRangeTabbedComponent', () => {
|
||||
expect(component.combinedDisplayValue).toBe('');
|
||||
expect(component.displayValue$.next).toHaveBeenCalledWith('');
|
||||
expect(component.context.queryFragments['dateRange']).toEqual('');
|
||||
expect(component.context.update).toHaveBeenCalled();
|
||||
expect(component.context.execute).toHaveBeenCalled();
|
||||
component.fields.forEach((field) => expect(component.context.filterRawParams[field]).toBeUndefined());
|
||||
done();
|
||||
});
|
||||
|
||||
+1
-1
@@ -138,7 +138,7 @@ export class SearchDateRangeTabbedComponent implements SearchWidget, OnInit {
|
||||
this.context.queryFragments[this.id] = this.combinedQuery;
|
||||
this.displayValue$.next(this.combinedDisplayValue);
|
||||
if (this.id && this.context && updateContext) {
|
||||
this.context.update();
|
||||
this.context.execute();
|
||||
}
|
||||
}
|
||||
onDateRangedValueChanged(value: Partial<SearchDateRange>, field: string) {
|
||||
|
||||
+4
-4
@@ -46,7 +46,7 @@ describe('SearchDatetimeRangeComponent', () => {
|
||||
},
|
||||
filterRawParams: {},
|
||||
populateFilters: new ReplaySubject(1),
|
||||
update: jasmine.createSpy('update')
|
||||
execute: jasmine.createSpy('execute')
|
||||
} as any;
|
||||
component.settings = { field: 'cm:created' };
|
||||
});
|
||||
@@ -128,7 +128,7 @@ describe('SearchDatetimeRangeComponent', () => {
|
||||
component.reset();
|
||||
|
||||
expect(component.context.queryFragments.createdDatetimeRange).toEqual('');
|
||||
expect(component.context.update).toHaveBeenCalled();
|
||||
expect(component.context.execute).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should update the query in UTC format when values change', async () => {
|
||||
@@ -150,7 +150,7 @@ describe('SearchDatetimeRangeComponent', () => {
|
||||
|
||||
expect(component.context.queryFragments[component.id]).toEqual(expectedQuery);
|
||||
expect(component.context.filterRawParams[component.id]).toEqual({ start: expectedFromDate, end: expectedToDate });
|
||||
expect(component.context.update).toHaveBeenCalled();
|
||||
expect(component.context.execute).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be able to update the query in UTC format from a GMT format', async () => {
|
||||
@@ -175,7 +175,7 @@ describe('SearchDatetimeRangeComponent', () => {
|
||||
|
||||
expect(startDate).toContain('2021-02-24');
|
||||
expect(endDate).toContain('2021-02-28');
|
||||
expect(component.context.update).toHaveBeenCalled();
|
||||
expect(component.context.execute).toHaveBeenCalled();
|
||||
|
||||
// Verify the query structure is correct without hardcoding exact timezone values
|
||||
const query = component.context.queryFragments[component.id];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user