Compare commits

..
Author SHA1 Message Date
VitoAlbano 97106892be [MIGRATION] - Fixed some issues for project and package json 2024-08-19 23:08:10 +01:00
VitoAlbano 9c435d0821 [ci:force] 2024-08-19 22:37:47 +01:00
Wojciech Duda 57d469915f [MIGRATION] - change selector for expanded expansion panel content 2024-08-19 22:31:10 +01:00
Wojciech Duda 88949621fb [MIGRATION] - storybook chips related e2e fails fix, unit alignments 2024-08-19 22:31:03 +01:00
VitoAlbano 8344c98741 [MIGRATION] - Removed wrong comment from e2e 2024-08-19 22:30:57 +01:00
VitoAlbano 960ae2f433 [MIGRATION] - Fixed outdated deps 2024-08-19 22:30:49 +01:00
VitoAlbano 75119fd758 [MIGRATION] - Project file restored 2024-08-19 22:30:42 +01:00
VitoAlbano 02809b6f27 [MIGRATION] - Rebased and added a comment to check why script is failing 2024-08-19 22:30:34 +01:00
VitoAlbano 1e877dd0c9 [MIGRATION] - Launched nx repair 2024-08-19 22:30:28 +01:00
VitoAlbano 809808d23a [MIGRATION] - Are we able to run a test? let's see 2024-08-19 22:30:22 +01:00
VitoAlbano 8508dfe24e [MIGRATION] - Problem on cli when used - probably misconfiguration 2024-08-19 22:30:14 +01:00
VitoAlbano cff291b11b [MIGRATION] - Problem on cli when used - probably misconfiguration 2024-08-19 22:30:07 +01:00
VitoAlbano a5f6210aa5 [MIGRATION] - I forgot npx 2024-08-19 22:30:00 +01:00
VitoAlbano 8ad83a5cb0 [MIGRATION] - Changed command for affected libs 2024-08-19 22:29:53 +01:00
VitoAlbano ece00f77da [MIGRATION] - Reverting js-api bundle remove 2024-08-19 22:29:47 +01:00
VitoAlbano 530b2be382 [MIGRATION] - Solving a funny comment issue part 2 2024-08-19 22:29:39 +01:00
VitoAlbano 3e00a1fe65 [MIGRATION] - Solving a funny comment issue 2024-08-19 22:29:31 +01:00
VitoAlbano 1c339a926b [MIGRATION] - Fixed lint errors for empty function 2024-08-19 22:29:24 +01:00
VitoAlbano cf175c67b8 [MIGRATION] - Improving the testing build to avoid having to bundle also the js-api 2024-08-19 22:29:17 +01:00
VitoAlbano 92823bd8ab Readded js-api bundle as it's needed for testing package for now 2024-08-19 22:29:08 +01:00
VitoAlbano fcf848e23f [MIGRATION] - added reference to @types also to general node_modules 2024-08-19 22:29:01 +01:00
VitoAlbano 7efd4a5238 [ci:force] - Added some changes and fixed lock 2024-08-19 22:28:34 +01:00
VitoAlbano 3011a67a16 NG16 - fixed the builds 2024-08-19 22:28:19 +01:00
VitoAlbano c521a90fd8 NG-16 - migration step 1 2024-08-19 22:28:11 +01:00
VitoAlbano 8923e7f7a4 [ci:force] - Script update permissions 2024-08-19 22:23:44 +01:00
4722 changed files with 163357 additions and 127970 deletions
+25
View File
@@ -0,0 +1,25 @@
.git
.github
.vscode
assets
coverage
docs
e2e
e2e-output
node_modules
scripts
src
lib
integration
tools
demo-shell/src/
demo-shell/resources/
/angular.json
/desktop.ini
/cspell.json
/CODE_OF_CONDUCT.md
/.stylelintignore
/ALFRESCOCORS.md
/CONTRIBUTING.md
/appveyor.yml
/BROWSER-SUPPORT.md
-3
View File
@@ -9,6 +9,3 @@ scripts
/angular.json
docs/**/*.md
lib/js-api/docs/**/*.md
.storybook
webpack.config.js
lib/core/src/lib/icon
+18 -61
View File
@@ -1,7 +1,16 @@
module.exports = {
root: true,
ignorePatterns: ['projects/**/*', '**/node_modules/**/*', 'lib/cli/node_modules/**/*', '**/node_modules', '**/assets', '**/scripts', '**/docs'],
ignorePatterns: [
'projects/**/*',
'**/node_modules/**/*',
'lib/cli/node_modules/**/*',
'**/node_modules',
'**/docker',
'**/assets',
'**/scripts',
'**/docs'
],
plugins: ['@nx'],
@@ -9,7 +18,7 @@ module.exports = {
{
files: ['*.ts'],
parserOptions: {
project: ['tsconfig.json'],
project: ['tsconfig.json', 'e2e/tsconfig.e2e.json'],
createDefaultProgram: true
},
extends: [
@@ -19,11 +28,6 @@ module.exports = {
'plugin:@angular-eslint/recommended',
'plugin:@angular-eslint/template/process-inline-templates',
'plugin:jsdoc/recommended-typescript-error'
// Uncomment this once all linting issues are fixed
// Note to developers:
// you can uncomment the full ruleset locally when fixing issues, and then comment
// that will allow splitting the work into smaller chunks
// 'plugin:unicorn/recommended'
],
plugins: [
'eslint-plugin-unicorn',
@@ -68,7 +72,6 @@ module.exports = {
],
'@angular-eslint/no-host-metadata-property': 'off',
'@angular-eslint/no-input-prefix': 'error',
'@angular-eslint/prefer-inject': 'error',
'@typescript-eslint/consistent-type-definitions': 'error',
'@typescript-eslint/dot-notation': 'off',
'@typescript-eslint/explicit-member-accessibility': [
@@ -79,7 +82,6 @@ module.exports = {
],
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/prefer-optional-chain': 'warn',
'@typescript-eslint/prefer-readonly': 'error',
'@typescript-eslint/no-inferrable-types': 'off',
'@typescript-eslint/no-require-imports': 'off',
'@typescript-eslint/no-var-requires': 'error',
@@ -101,10 +103,10 @@ module.exports = {
}
],
'@typescript-eslint/member-ordering': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'prefer-arrow/prefer-arrow-functions': 'off',
'prefer-promise-reject-errors': 'error',
'brace-style': 'off',
'@typescript-eslint/brace-style': 'error',
'comma-dangle': 'error',
'default-case': 'error',
'import/order': 'off',
@@ -142,23 +144,13 @@ module.exports = {
],
'no-duplicate-imports': 'error',
'no-multiple-empty-lines': 'error',
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': ['off', { ignoreDeclarationMerge: true }],
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_'
}
],
'no-redeclare': 'error',
'no-return-await': 'error',
'rxjs/no-create': 'error',
'rxjs/no-subject-unsubscribe': 'error',
'rxjs/no-subject-value': 'error',
'rxjs/no-unsafe-takeuntil': 'error',
'unicorn/filename-case': 'error',
'unicorn/prefer-optional-catch-binding': 'error',
'@typescript-eslint/no-unused-expressions': [
'error',
{
@@ -171,7 +163,7 @@ module.exports = {
[
'/*!',
' * @license',
' * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.',
' * Copyright © 2005-2024 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.',
@@ -187,60 +179,25 @@ module.exports = {
' */'
]
],
'no-restricted-syntax': [
'error',
{
selector: "Identifier[name='CUSTOM_ELEMENTS_SCHEMA']",
message: 'The use of CUSTOM_ELEMENTS_SCHEMA is not allowed. Consider alternatives for proper schema handling.'
},
{
selector: "Identifier[name='NO_ERRORS_SCHEMA']",
message: 'The use of NO_ERRORS_SCHEMA is not allowed. Consider alternatives for proper schema handling.'
},
{
selector: 'TSEnumDeclaration',
message: 'Enums are not allowed. Use string literal types (e.g., type Foo = "a" | "b") or const objects instead.'
},
{
selector: ':matches(Literal[value=/ng-reflect-/], TemplateElement[value.cooked=/ng-reflect-/])',
message: '*ng-reflect-* attributes should not be used. Consider alternatives for proper selectors.'
}
]
'@nx/dependency-checks': 'warn'
}
},
{
files: ['*.html'],
extends: ['plugin:@angular-eslint/template/recommended', 'plugin:@angular-eslint/template/accessibility'],
parser: '@angular-eslint/template-parser',
rules: {
'@angular-eslint/template/prefer-self-closing-tags': 'error'
}
rules: {}
},
{
files: ['*.spec.ts'],
plugins: ['@alfresco/eslint-angular'],
rules: {
'@alfresco/eslint-angular/no-angular-material-selectors': 'error',
'@angular-eslint/component-class-suffix': 'off'
'@alfresco/eslint-angular/no-angular-material-selectors': 'error'
}
},
{
files: ['*.ts'],
extends: ['plugin:@angular-eslint/template/process-inline-templates'],
excludedFiles: ['*.spec.ts']
},
{
files: ['*.json'],
parser: 'jsonc-eslint-parser',
rules: {
'comma-dangle': ['error', 'never']
}
},
{
files: ['**/*.stories.ts'],
rules: {
'@cspell/spellchecker': 'off'
}
}
],
+3 -3
View File
@@ -4,15 +4,15 @@
# the repo. Unless a later match takes precedence,
# these users will be requested for
# review when someone opens a pull request.
* @eromano
* @eromano @popovicsandras @DenysVuika
# Order is important; the last matching pattern takes the most
# precedence. When someone opens a pull request that only
# modifies JS files, only @js-owner and not the global
# owner(s) will be requested for a review.
/e2e/ @eromano
/e2e/ @eromano @cristinaj
# The `docs/*` pattern will match files like
# `docs/getting-started.md` but not further nested files like
# `docs/build-app/troubleshooting.md`.
/docs/ @eromano
/docs/ @m-hulbert @eromano
@@ -0,0 +1,92 @@
name: Append content to Artifact
description: 'Allow the user to append content to an existing artifact'
inputs:
artifact-name:
description: 'The name of the artifact'
required: true
type: string
file-name:
description: 'The name of the file with extension created in the artifact'
required: true
type: string
content:
description: 'The content to append'
type: string
default: ""
runs:
using: "composite"
steps:
- run: echo "Artifact Append"
shell: bash
- name: Download artifact
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
with:
name: ${{ inputs.artifact-name }}
pattern: ${{ inputs.artifact-name }}-*
merge-multiple: true
- run: ls
shell: bash
- name: Append content
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
env:
contentFile: ${{ inputs.content }}
fileName: ${{ inputs.file-name }}
with:
script: |
const fs = require('fs');
const affectedLib = process.env.contentFile;
const fileName = process.env.fileName;
core.info(`Input Filename: ${fileName}`);
core.info(`Input content: ${affectedLib}`);
const content = read(fileName)
core.info(`File content: ${content}`);
appendContent(content, affectedLib);
function read(filename) {
try {
const contentFile = fs.readFileSync(filename, 'utf8').replace('\n','');
return contentFile;
} catch (err) {
core.error(err);
}
}
function write(filename, content) {
try {
fs.writeFileSync(filename, content);
} catch (err) {
core.error(err);
}
}
function appendContent(content, append) {
let changedContent;
const libs = content.split(' ');
if (libs?.length>0) {
if (libs.length === 1 && libs[0] === '') {
libs[0] = append;
changedContent = libs[0];
}
else if (!libs.includes(append)) {
libs.push(append);
changedContent = libs.join(' ');
} else {
core.info(`Lib ${append} already affected`);
}
}
if (changedContent != undefined){
core.info(`File content append: ${changedContent}`)
write(fileName, changedContent);
}
}
- name: Upload artifact
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
with:
name: ${{ inputs.artifact-name }}
path: ${{ inputs.file-name }}
@@ -0,0 +1,42 @@
name: Extract Artifact
description: 'Allow the user to extract content from an artifact'
inputs:
artifact-name:
description: 'The name of the artifact'
required: true
type: string
file-name:
description: 'The name of the file with extension created in the artifact'
required: true
type: string
content:
description: 'The init content the file should have'
type: string
default: ""
outputs:
result:
description: "the value extrated from the file inside the artifact"
value: ${{ steps.extract.outputs.result }}
runs:
using: "composite"
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- run: echo "Artifact Extract"
shell: bash
- name: Download artifact
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
with:
name: ${{ inputs.artifact-name }}
pattern: ${{ inputs.artifact-name }}-*
merge-multiple: true
- id: extract
shell: bash
run: |
value=`cat ${{ inputs.file-name }}`
echo "print $value"
echo "result=$value" >> $GITHUB_OUTPUT
@@ -0,0 +1,32 @@
name: Initialize Artifact
description: 'Allow the user to initialize an empty artifact used globally'
inputs:
artifact-name:
description: 'The name of the artifact'
required: true
type: string
file-name:
description: 'The name of the file with extension created in the artifact'
required: true
type: string
content:
description: 'The init content the file should have'
type: string
default: ""
runs:
using: "composite"
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Create empty artifact
shell: bash
run:
echo "${{inputs.content}}" > ${{ inputs.file-name }}
- name: Upload artifact
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
with:
name: ${{ inputs.artifact-name }}
path: ${{ inputs.file-name }}
+8 -4
View File
@@ -9,6 +9,12 @@ inputs:
runs:
using: "composite"
steps:
- name: Before install
if: ${{ ! env.ACT }}
shell: bash
run: |
pip install --user --quiet awscli
- name: base vars
shell: bash
run: |
@@ -45,13 +51,11 @@ runs:
- name: RELEASE on master/develop patch branch
if: ${{ env.BREAK_ACTION == false && github.event.pull_request.merged }}
shell: bash
env:
REF_NAME: ${{ github.ref_name }}
run: |
if [[ "$REF_NAME" =~ ^master(-patch.*)?$ ]]; then
if [[ "${{ github.ref_name }}" =~ ^master(-patch.*)?$ ]]; then
# into master(-patch*)
echo "Setting up CI flags for Push on master patch"
elif [[ "$REF_NAME" =~ ^develop-patch.*$ ]]; then
elif [[ "${{ github.ref_name }}" =~ ^develop-patch.*$ ]]; then
# into develop-patch*
echo "Setting up CI flags for Push develop patch"
else
-23
View File
@@ -1,23 +0,0 @@
name: Git tag creation
description: 'Creates a git tag in the specified repository for a given commit.'
inputs:
tagName:
description: 'The github tag to be created'
required: true
releaseNote:
description: 'The github release note to be created'
required: false
runs:
using: 'composite'
steps:
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
name: Create git tag
env:
TAG_NAME: ${{ inputs.tagName }}
RELEASE_NOTE: ${{ inputs.releaseNote }}
with:
script: |
const createGitTag = require('./.github/actions/create-git-tag/create-git-tag.js');
await createGitTag({ core, github, context, tagName: process.env.TAG_NAME });
@@ -1,42 +0,0 @@
/*!
* @license
* Copyright © 2005-2023 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.
*/
module.exports = async ({ github, context, core, tagName }) => {
const tagSHA = context.payload?.after ?? context.sha;
core.notice(`Creating tag with title: ${tagName}, and SHA: ${tagSHA}`);
const createdTag = await github.rest.git.createTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag: tagName,
message: 'Release note',
object: tagSHA,
type: 'commit'
});
const createdRef = await github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'refs/tags/' + tagName,
sha: createdTag.data.sha
});
if (createdRef.status === 201) {
core.notice(`Tag ${tagName} was created successfully`);
}
};
@@ -3,33 +3,17 @@ description: "Download build artifacts"
runs:
using: "composite"
steps:
- name: Restore dist from cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: dist
key: dist-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
dist-${{ github.run_id }}-
fail-on-cache-miss: true
- name: Restore nxcache from cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: nxcache
key: nxcache-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
nxcache-${{ github.run_id }}-
fail-on-cache-miss: true
- name: Restore node_modules from cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: node_modules
key: node-modules-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
node-modules-${{ github.run_id }}-
fail-on-cache-miss: true
- name: download and extract artifacts from s3
shell: bash
env:
REMOTE_PATH: "alfresco-ng2-components/build-cache/${{ github.run_id }}"
run: |
packages=( dist nxcache node_modules )
for i in "${packages[@]}"; do
time aws s3 cp --no-progress s3://${S3_BUILD_BUCKET_SHORT_NAME}/${REMOTE_PATH}/$i.tar.gz $i.tar.gz
du -h $i.tar.gz
time tar xzf $i.tar.gz
done
- name: show files
shell: bash
run: |
@@ -40,4 +24,4 @@ runs:
echo "====NXCACHE===="
find nxcache -maxdepth 1 -type d
echo "====ADF===="
find node_modules/@alfresco/ -maxdepth 1 -type d
find node_modules/@alfresco/ -maxdepth 1 -type d
+204
View File
@@ -0,0 +1,204 @@
name: "e2e"
description: "e2e"
inputs:
e2e-test-id:
description: "Test id"
required: true
e2e-artifact-id:
description: "Artifact id"
required: true
e2e-test-folder:
description: "Test folder"
required: true
e2e-test-provider:
description: "Test provider"
required: true
e2e-test-auth:
description: "Test auth"
required: true
output:
description: "Output path"
required: true
check-cs-env:
required: true
description: check cs env
default: "false"
check-ps-env:
required: true
description: check ps env
default: "false"
check-external-cs-env:
required: true
description: check external cs env
default: "false"
check-ps-cloud-env:
required: true
description: check ps cloud env
default: "false"
e2e-tar-name: #
description: tarball name
required: false
default: e2e.tar.gz
apa-proxy: #
description: "proxy host"
required: true
deps:
description: "Library dependencies"
required: false
default: ""
runs:
using: "composite"
steps:
- name: Determine if affected
shell: bash
id: determine-affected
run: |
isAffected=false
affectedLibs=$(npx nx print-affected --type=lib --select=projects ${NX_CALCULATION_FLAGS} --plain)
if [[ $affectedLibs =~ "${{ inputs.e2e-test-id }}" ]]; then
isAffected=true
fi;
echo "Determine if ${{ inputs.e2e-test-id }} is affected: $isAffected";
echo "isAffected=$isAffected" >> $GITHUB_OUTPUT
- name: print value
shell: bash
run: |
echo "value: ${{ steps.determine-affected.outputs.isAffected }}"
- name: use APA as PROXY host if apa-proxy is set
shell: bash
run: |
if [[ -n "${{ inputs.apa-proxy }}" ]]; then
echo "APA proxy set."
echo "PROXY_HOST_BPM=${E2E_HOST_APA}" >> $GITHUB_ENV
echo "PROXY_HOST_ECM=${E2E_IDENTITY_HOST_APA}" >> $GITHUB_ENV
echo "HOST_SSO=${E2E_IDENTITY_HOST_APA}" >> $GITHUB_ENV
fi
- name: install aws cli
shell: bash
run: pip install awscli
- name: download smartrunner test results from s3 bucket if they exist
shell: bash
env:
REMOTE_PATH: smart-runner/${{ github.run_id}}/${{ inputs.e2e-test-folder }}-${{ inputs.e2e-artifact-id}}/e2e.tar.gz
run: |
set -u;
mkdir -p "${SMART_RUNNER_PATH}"
if [[ $(aws s3 ls "s3://${S3_BUILD_BUCKET_SHORT_NAME}/adf/${REMOTE_PATH}" > /dev/null; echo $?) -eq 0 ]]; then
echo "downloading test files"
aws s3 cp "s3://${S3_BUILD_BUCKET_SHORT_NAME}/adf/${REMOTE_PATH}" .;
tar xzf ${{ inputs.e2e-tar-name }};
else
echo "nothing to download";
fi
- name: check EXTERNAL-CS is UP
shell: bash
if: ${{ inputs.check-external-cs-env == 'true' && steps.determine-affected.outputs.isAffected == 'true' }}
run: |
echo "running: check EXTERNAL-CS is UP"
set -u;
./node_modules/@alfresco/adf-cli/bin/adf-cli \
check-cs-env \
--host "$EXTERNAL_ACS_HOST" \
-u "$E2E_USERNAME" \
-p "$E2E_PASSWORD" || exit 1
- name: Check CS is UP
shell: bash
if: ${{ inputs.check-cs-env == 'true' && steps.determine-affected.outputs.isAffected == 'true' }}
run: |
echo "Running: Check CS is UP"
set -u;
./node_modules/@alfresco/adf-cli/bin/adf-cli \
check-cs-env \
--host "$E2E_HOST" \
-u "$E2E_USERNAME" \
-p "$E2E_PASSWORD" || exit 1
- name: check PS is UP
shell: bash
if: ${{ inputs.check-ps-env == 'true' && steps.determine-affected.outputs.isAffected == 'true' }}
run: |
echo "Running: check PS is UP"
set -u;
./node_modules/@alfresco/adf-cli/bin/adf-cli init-aps-env \
--host "$E2E_HOST" \
-u "$E2E_USERNAME" \
-p "$E2E_PASSWORD" \
--license "$AWS_S3_BUCKET_ACTIVITI_LICENSE" || exit 1
- name: check PS-CLOUD is UP
shell: bash
if: ${{ inputs.check-ps-cloud-env == 'true' && steps.determine-affected.outputs.isAffected == 'true' }}
run: |
echo "running: check PS-CLOUD is UP"
set -u;
./node_modules/@alfresco/adf-cli/bin/adf-cli init-aae-env \
--oauth "$E2E_IDENTITY_HOST_APA" \
--host "$E2E_HOST_APA" \
--modelerUsername "$E2E_MODELER_USERNAME" \
--modelerPassword "$E2E_MODELER_PASSWORD" \
--devopsUsername "$E2E_DEVOPS_USERNAME" \
--devopsPassword "$E2E_DEVOPS_PASSWORD" \
--clientId 'alfresco' || exit 1
- name: variables sanitization
env:
FOLDER: "${{ inputs.e2e-test-folder }}"
PROVIDER: "${{ inputs.e2e-test-provider }}"
AUTH_TYPE: "${{ inputs.e2e-test-auth }}"
E2E_TEST_ID: "${{ inputs.e2e-test-id }}"
DEPS: "${{ inputs.deps }}"
shell: bash
run: |
set -u;
echo $PROXY_HOST_BPM
echo "GIT_HASH=$GIT_HASH" >> $GITHUB_ENV
- name: run test
id: e2e_run
if: ${{ steps.determine-affected.outputs.isAffected == 'true' }}
env:
FOLDER: "${{ inputs.e2e-test-folder }}"
PROVIDER: "${{ inputs.e2e-test-provider }}"
AUTH_TYPE: "${{ inputs.e2e-test-auth }}"
E2E_TEST_ID: "${{ inputs.e2e-test-id }}"
DEPS: "${{ inputs.deps }}"
shell: bash
run: |
set -u;
if [[ ${{ inputs.e2e-test-folder }} == 'content-services/upload' ]]; then
export DISPLAY=:99
chromedriver --url-base=/wd/hub &
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional
bash ./scripts/github/e2e/e2e.sh "$E2E_TEST_ID" "$DEPS" "browser" || exit 1
else
bash ./scripts/github/e2e/e2e.sh "$E2E_TEST_ID" "$DEPS" || exit 1
fi
- name: Trace failing e2e
if: ${{ steps.determine-affected.outputs.isAffected == 'true' && github.event_name == 'schedule' && failure() }}
uses: ./.github/actions/artifact-append
with:
artifact-name: "global-e2e-result-${{ inputs.e2e-test-id }}"
file-name: e2e-failures.txt
content: "${{ inputs.e2e-test-id }}"
- name: upload artifacts on gh
id: upload_gh
if: ${{ steps.determine-affected.outputs.isAffected == 'true' }}
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
with:
name: e2e-artifact-output-${{inputs.e2e-artifact-id}}
path: /home/runner/work/alfresco-ng2-components/alfresco-ng2-components/e2e-output-*
- name: upload smart-runner tests results on s3 to cache tests
shell: bash
if: always()
env:
REMOTE_PATH: "smart-runner/${{ github.run_id}}/${{ inputs.e2e-test-folder }}-${{inputs.e2e-artifact-id}}/e2e.tar.gz"
# description: always upload newer results
run: |
tar czf "${{ inputs.e2e-tar-name }}" "${SMART_RUNNER_PATH}"
aws s3 cp "${{ inputs.e2e-tar-name }}" "s3://${S3_BUILD_BUCKET_SHORT_NAME}/adf/${REMOTE_PATH}"
+1 -3
View File
@@ -20,10 +20,8 @@ runs:
- name: set dryrun flag to TRUE
shell: bash
id: dryrun
env:
DRY_RUN_FLAG: ${{ inputs.dry-run-flag }}
run: |
if [[ '$DRY_RUN_FLAG' == 'true' ]]; then
if [[ '${{ inputs.dry-run-flag }}' == 'true' ]]; then
echo "dryrun=--dryrun" >> $GITHUB_OUTPUT;
echo "enabling dryrun"
else
+2 -7
View File
@@ -5,27 +5,22 @@ inputs:
branch_name:
description: "override GITHUB_REF_NAME"
required: false
default: ${{ github.ref_name }}
default: $GITHUB_REF_NAME
runs:
using: "composite"
steps:
- name: load "NPM TAG"
id: set-npm-tag
uses: ./.github/actions/set-npm-tag
with:
branch_name: ${{ inputs.branch_name }}
- name: check npm bundle
shell: bash
id: sha_out
env:
TAG_NPM: ${{ steps.set-npm-tag.outputs.npm-tag }}
run: |
if [[ -z $TAG_NPM ]]; then
echo "TAG_NPM not set, aborting"
exit 1
fi
ADF_VERSION=$(npm view @alfresco/adf-core@${TAG_NPM} version)
echo "check bundle on TAG_NPM='${TAG_NPM}' and ADF_VERSION='${ADF_VERSION}'"
./scripts/github/build/npm-check-bundles.sh -v ${ADF_VERSION}
./scripts/github/build/npm-check-bundles.sh
@@ -0,0 +1,21 @@
name: "print affected libs"
description: "Retrive the affected libs based on the based and head input"
inputs:
base:
description: 'the base sha'
required: true
type: string
default: main
head:
description: 'the head sha'
required: true
type: string
default: HEAD
output:
affected:
description: 'the affected libs of the repo'
affected_is_empty:
description: 'true or false accondingly'
runs:
using: "node16"
main: 'dist/index.js'
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+476
View File
@@ -0,0 +1,476 @@
{
"name": "print-affected-libs",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "print-affected-libs",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/exec": "^1.1.1",
"@actions/github": "^5.1.1"
},
"devDependencies": {
"@vercel/ncc": "^0.36.1"
}
},
"node_modules/@actions/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
}
},
"node_modules/@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"dependencies": {
"@actions/io": "^1.0.1"
}
},
"node_modules/@actions/github": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
"integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"@octokit/core": "^3.6.0",
"@octokit/plugin-paginate-rest": "^2.17.0",
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
}
},
"node_modules/@actions/http-client": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz",
"integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==",
"dependencies": {
"tunnel": "^0.0.6"
}
},
"node_modules/@actions/io": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="
},
"node_modules/@octokit/auth-token": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
"integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
"dependencies": {
"@octokit/types": "^6.0.3"
}
},
"node_modules/@octokit/core": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
"dependencies": {
"@octokit/auth-token": "^2.4.4",
"@octokit/graphql": "^4.5.8",
"@octokit/request": "^5.6.3",
"@octokit/request-error": "^2.0.5",
"@octokit/types": "^6.0.3",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/endpoint": {
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
"integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
"dependencies": {
"@octokit/types": "^6.0.3",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/graphql": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
"dependencies": {
"@octokit/request": "^5.6.0",
"@octokit/types": "^6.0.3",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/openapi-types": {
"version": "12.11.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz",
"integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="
},
"node_modules/@octokit/plugin-paginate-rest": {
"version": "2.21.3",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz",
"integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==",
"dependencies": {
"@octokit/types": "^6.40.0"
},
"peerDependencies": {
"@octokit/core": ">=2"
}
},
"node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "5.16.2",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz",
"integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==",
"dependencies": {
"@octokit/types": "^6.39.0",
"deprecation": "^2.3.1"
},
"peerDependencies": {
"@octokit/core": ">=3"
}
},
"node_modules/@octokit/request": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
"dependencies": {
"@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.1.0",
"@octokit/types": "^6.16.1",
"is-plain-object": "^5.0.0",
"node-fetch": "^2.6.7",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/request-error": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
"dependencies": {
"@octokit/types": "^6.0.3",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
},
"node_modules/@octokit/types": {
"version": "6.41.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz",
"integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==",
"dependencies": {
"@octokit/openapi-types": "^12.11.0"
}
},
"node_modules/@vercel/ncc": {
"version": "0.36.1",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.36.1.tgz",
"integrity": "sha512-S4cL7Taa9yb5qbv+6wLgiKVZ03Qfkc4jGRuiUQMQ8HGBD5pcNRnHeYM33zBvJE4/zJGjJJ8GScB+WmTsn9mORw==",
"dev": true,
"bin": {
"ncc": "dist/ncc/cli.js"
}
},
"node_modules/before-after-hook": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
},
"node_modules/deprecation": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
},
"node_modules/is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/node-fetch": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
"integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"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==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
"engines": {
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
},
"node_modules/universal-user-agent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"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=="
},
"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==",
"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=="
}
},
"dependencies": {
"@actions/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"requires": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
}
},
"@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"requires": {
"@actions/io": "^1.0.1"
}
},
"@actions/github": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
"integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
"requires": {
"@actions/http-client": "^2.0.1",
"@octokit/core": "^3.6.0",
"@octokit/plugin-paginate-rest": "^2.17.0",
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
}
},
"@actions/http-client": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz",
"integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==",
"requires": {
"tunnel": "^0.0.6"
}
},
"@actions/io": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="
},
"@octokit/auth-token": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
"integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
"requires": {
"@octokit/types": "^6.0.3"
}
},
"@octokit/core": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
"requires": {
"@octokit/auth-token": "^2.4.4",
"@octokit/graphql": "^4.5.8",
"@octokit/request": "^5.6.3",
"@octokit/request-error": "^2.0.5",
"@octokit/types": "^6.0.3",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/endpoint": {
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
"integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
"requires": {
"@octokit/types": "^6.0.3",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/graphql": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
"requires": {
"@octokit/request": "^5.6.0",
"@octokit/types": "^6.0.3",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/openapi-types": {
"version": "12.11.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz",
"integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="
},
"@octokit/plugin-paginate-rest": {
"version": "2.21.3",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz",
"integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==",
"requires": {
"@octokit/types": "^6.40.0"
}
},
"@octokit/plugin-rest-endpoint-methods": {
"version": "5.16.2",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz",
"integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==",
"requires": {
"@octokit/types": "^6.39.0",
"deprecation": "^2.3.1"
}
},
"@octokit/request": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
"requires": {
"@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.1.0",
"@octokit/types": "^6.16.1",
"is-plain-object": "^5.0.0",
"node-fetch": "^2.6.7",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/request-error": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
"requires": {
"@octokit/types": "^6.0.3",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
},
"@octokit/types": {
"version": "6.41.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz",
"integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==",
"requires": {
"@octokit/openapi-types": "^12.11.0"
}
},
"@vercel/ncc": {
"version": "0.36.1",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.36.1.tgz",
"integrity": "sha512-S4cL7Taa9yb5qbv+6wLgiKVZ03Qfkc4jGRuiUQMQ8HGBD5pcNRnHeYM33zBvJE4/zJGjJJ8GScB+WmTsn9mORw==",
"dev": true
},
"before-after-hook": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
},
"deprecation": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
},
"is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
},
"node-fetch": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
"integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
"requires": {
"whatwg-url": "^5.0.0"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"requires": {
"wrappy": "1"
}
},
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
},
"universal-user-agent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"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=="
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}
}
}
@@ -0,0 +1,20 @@
{
"name": "print-affected-libs",
"version": "1.0.0",
"description": "",
"main": "dist/index.js",
"scripts": {
"build": "ncc build src/index.js -o dist --source-map"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/exec": "^1.1.1",
"@actions/github": "^5.1.1"
},
"devDependencies": {
"@vercel/ncc": "^0.36.1"
}
}
@@ -0,0 +1,32 @@
const core = require('@actions/core');
const exec = require('@actions/exec');
async function run() {
const base = core.getInput('base', {required: false }) || '5288597580';
core.setOutput('affected_is_empty', 'true');
let affected = '';
let myError = '';
const options = {};
options.listeners = {
stdout: (data) => {
affected += data.toString()
},
stderr: (data) => {
myError += data.toString();
}
};
await exec.exec(`npx nx print-affected --target=build --select=tasks.target.project --base=${base} --head=${head}`, [], options)
const affectedTrimmed = affected.trim();
if (affectedTrimmed !== '') {
core.setOutput('affected_is_empty', 'false');
}
core.notice(`Retriving affected libs: ${affectedTrimmed}`);
core.setOutput('affected', affectedTrimmed);
}
run();
-17
View File
@@ -1,17 +0,0 @@
name: 'Save Nx Cache'
description: 'Save Nx cache and dist outputs only when the job succeeds'
inputs:
cache-suffix:
description: 'Must match the cache-suffix used in the setup action'
required: true
runs:
using: "composite"
steps:
- name: Save nx cache
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
nxcache
.nx
dist
key: ${{ runner.os }}-nxcache-${{ inputs.cache-suffix }}-${{ hashFiles('nx.json') }}-${{ github.sha }}
+21 -29
View File
@@ -2,44 +2,36 @@ name: "set npm tag"
description: "se NPM tag"
inputs:
event_name:
description: "override github.event_name"
required: false
default: ${{ github.event_name }}
branch_name:
description: "override GITHUB_REF_NAME"
required: false
default: ${{ github.ref_name }}
outputs:
npm-tag:
description: "NPM tag"
value: ${{ steps.set-npm-tag.outputs.npm-tag }}
default: $GITHUB_REF_NAME
runs:
using: "composite"
steps:
- name: set TAG_NPM
id: set-npm-tag
shell: bash
env:
BRANCH_NAME: ${{ inputs.branch_name }}
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
TAG_NPM="branch"
else
TAG_NPM="alpha"
VERSION_IN_PACKAGE_JSON=$(node -p "require('./package.json')".version)
echo "version in package.json=${VERSION_IN_PACKAGE_JSON}"
if [[ $BRANCH_NAME =~ ^master(-patch.*)?$ ]]; then
# Pre-release versions
if [[ $VERSION_IN_PACKAGE_JSON =~ ^[0-9]*\.[0-9]*\.[0-9]*-A\.[0-9]*$ ]]; then
TAG_NPM=next
# Stable major versions
else
TAG_NPM=latest
fi
fi
if [[ $BRANCH_NAME =~ ^develop(-patch.*)?$ ]]; then
TAG_NPM=alpha
fi
TAG_NPM="alpha"
VERSION_IN_PACKAGE_JSON=$(node -p "require('./package.json')".version)
echo "version in package.json=${VERSION_IN_PACKAGE_JSON}"
if [[ ${{ inputs.branch_name }} =~ ^master(-patch.*)?$ ]]; then
# Pre-release versions
if [[ $VERSION_IN_PACKAGE_JSON =~ ^[0-9]*\.[0-9]*\.[0-9]*-A\.[0-9]*$ ]];
then
TAG_NPM=next
# Stable major versions
else
TAG_NPM=latest
fi
fi
echo "npm-tag=$TAG_NPM" >> $GITHUB_OUTPUT
echo "Computed tag: $TAG_NPM"
if [[ ${{ inputs.branch_name }} =~ ^develop(-patch.*)?$ ]]; then
TAG_NPM=alpha
fi
echo "TAG_NPM=${TAG_NPM}" >> $GITHUB_ENV
+13
View File
@@ -0,0 +1,13 @@
name: 'Install Google Chrome'
description: 'Install Google Chrome'
runs:
using: "composite"
steps:
- name: Install google chrome
shell: bash
run: |
wget -q https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_114.0.5735.133-1_amd64.deb
sudo apt install -y --allow-downgrades ./google-chrome-stable_114.0.5735.133-1_amd64.deb
sudo ln -s /usr/bin/google-chrome /usr/bin/chrome
chrome --version
+26 -41
View File
@@ -1,17 +1,13 @@
name: 'Setup'
description: 'Initialize cache, env var load'
inputs:
enable-node-modules-cache:
description: 'enable caching for node modules'
enable-cache:
description: 'enable caching'
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
default: 'default'
full-setup:
description: 'Run git-latest-tag, npm-tag, and before-install (requires fetch-depth: 0). Set to false for matrix jobs that only need node/cache.'
enable-node-modules-cache:
description: 'enable caching for node modules'
required: false
type: boolean
default: 'true'
@@ -20,54 +16,43 @@ inputs:
required: false
type: boolean
default: 'false'
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
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version-file: '.nvmrc'
cache-dependency-path: package-lock.json
- 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
- name: load "NPM TAG"
if: ${{ inputs.full-setup == 'true' }}
id: set-npm-tag
uses: ./.github/actions/set-npm-tag
- name: Cache node modules
uses: Alfresco/alfresco-build-tools/.github/actions/git-latest-tag@9d00215f539f669b41c7fabf7b88ccc8f316926c # v6.0.1
# CACHE
- name: Node Modules cache
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: Restore nx cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
env:
cache-name: node-modules-cache
with:
path: |
nxcache
.nx
dist
key: ${{ runner.os }}-nxcache-${{ inputs.cache-suffix }}-${{ hashFiles('nx.json') }}-${{ github.sha }}
node_modules
key: .npm-${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-nxcache-${{ inputs.cache-suffix }}-${{ hashFiles('nx.json') }}-
${{ runner.os }}-nxcache-${{ inputs.cache-suffix }}-
node_modules-${{ runner.os }}-build-${{ env.cache-name }}-
node_modules-${{ runner.os }}-build-
node_modules-${{ runner.os }}-
- name: pip cache
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
if: ${{ inputs.enable-cache == 'true' }}
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-
restore-keys: |
${{ runner.os }}
- name: load "NPM TAG"
uses: ./.github/actions/set-npm-tag
- name: before install script
if: ${{ inputs.full-setup == 'true' }}
uses: ./.github/actions/before-install
with:
act: ${{ inputs.act }}
@@ -0,0 +1,49 @@
name: Identify the slack group
description: 'Identify the slack group area based on the affected'
inputs:
affected:
description: 'The name of the affected lib'
required: true
type: string
outputs:
groups:
description: "the slack groups"
value: ${{ steps.group.outputs.result }}
runs:
using: "composite"
steps:
- name: Append group
id: group
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
env:
affectedLib: ${{ inputs.affected }}
with:
script: |
const affectedLib = process.env.affectedLib;
core.info(`Input ${affectedLib}`);
const slackGroups = new Set();
const AlfrescoQAGroup = '@alfresco-testing-qa';
const HxpQAGroup = '@hxp-studio-qa-group';
const libs = affectedLib.split(' ')
if (libs.includes('content-services') || libs.includes('process-services')) {
slackGroups.add(AlfrescoQAGroup);
}
if (libs.includes('process-services-cloud')) {
slackGroups.add(HxpQAGroup);
}
if (libs.includes('core')) {
slackGroups.add(AlfrescoQAGroup);
slackGroups.add(HxpQAGroup);
}
const result = Array.from(slackGroups.values()).join(' ');
core.info(`Result ${result}`);
return result;
@@ -4,22 +4,16 @@ description: "Upload build artifacts"
runs:
using: "composite"
steps:
- name: Save dist to cache
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: dist
key: dist-${{ github.run_id }}-${{ github.run_attempt }}
- name: Save nxcache to cache
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
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
with:
path: node_modules
key: node-modules-${{ github.run_id }}-${{ github.run_attempt }}
- name: tar and upload artifacts
shell: bash
env:
REMOTE_PATH: "alfresco-ng2-components/build-cache/${{ github.run_id }}"
run: |
packages=( dist nxcache node_modules )
for i in "${packages[@]}"; do
time tar czf $i.tar.gz $i
du -h $i.tar.gz
time aws s3 cp --no-progress $i.tar.gz "s3://${S3_BUILD_BUCKET_SHORT_NAME}/${REMOTE_PATH}/$i.tar.gz"
done
+1
View File
@@ -3,3 +3,4 @@ name: "CodeQL config"
paths-ignore:
- 'e2e/resources/**'
- 'docs/**'
-78
View File
@@ -1,78 +0,0 @@
# General Code Review Instructions
## Review Priorities
When performing a code review, prioritize issues in the following order:
🔴 CRITICAL (Block merge)
* Security: Vulnerabilities, exposed secrets, authentication/authorization issues, injection, XSS
* Memory/resource leaks
* Crashes or undefined behavior
* Correctness: Logic errors, data corruption risks, race conditions, missing error handling
* Data Loss: Risk of data loss or corruption
🟡 IMPORTANT (Requires discussion)
* Code Quality: Severe violations of SOLID principles, excessive duplication
* Test Coverage: Missing tests for critical paths or new functionality
* Performance: Obvious performance bottlenecks (N+1 queries, memory leaks)
* Architecture: Significant deviations from established patterns
🟢 SUGGESTION (Non-blocking improvements)
* Readability: Poor naming, complex logic that could be simplified
* Optimization: Performance improvements without functional impact
* Best Practices: Minor deviations from conventions
* Documentation: Missing or incomplete comments/documentation
## General Review Principles
When performing a code review, follow these principles:
* Be specific: Reference exact lines, files, and provide concrete examples
* Be pragmatic: Not every suggestion needs immediate implementation
* Provide context: Explain WHY something is an issue and the potential impact
* Suggest solutions: Show corrected code when applicable, not just what's wrong
* Be constructive: Focus on improving the code, not criticizing the author
* Recognize good practices: Acknowledge well-written code and smart solutions
* Group related comments: Avoid multiple comments about the same topic
* Ask clarifying questions when code intent is unclear
## Code Quality Standards
When performing a code review, check for:
### Clean Code
* Descriptive and meaningful names for variables, functions, and classes
* Single Responsibility Principle: each function/class does one thing well
* DRY (Don't Repeat Yourself): no code duplication
* Functions should be small and focused (ideally < 20-30 lines)
* Avoid deeply nested code (max 3-4 levels), unit tests should be an exception from this rule
* Avoid magic numbers and strings (use constants)
* Code should be self-documenting; comments only when necessary
* Code follows consistent style and conventions
* No commented-out code or TODO without tickets
## Error Handling
* Proper error handling at appropriate levels
* Meaningful error messages
* No silent failures or ignored exceptions
* Fail fast: validate inputs early
* Use appropriate error types/exceptions
## Testing Standards
When performing a code review, verify test quality:
* Coverage: Critical paths and new functionality must have tests
* Test Names: Descriptive names that explain what is being tested
* Independence: Tests should not depend on each other or external state
* Assertions: Use specific assertions, avoid generics
* Edge Cases: Test boundary conditions, null values, empty inputs
* Mock Appropriately: Mock external dependencies, not domain logic
* New code has appropriate test coverage
* No tests that always pass or are commented out
## Architecture and Design
When performing a code review, verify architectural principles:
* Separation of Concerns: Clear boundaries between layers/modules
* Dependency Direction: High-level modules don't depend on low-level details
* Interface Segregation: Prefer small, focused interfaces
* Loose Coupling: Components should be independently testable
* High Cohesion: Related functionality grouped together
* Consistent Patterns: Follow established patterns in the codebase
+28 -3
View File
@@ -21,9 +21,6 @@ updates:
nrwl:
patterns:
- "@nrwl/*"
typescript-eslint:
patterns:
- "@typescript-eslint/*"
ignore:
- dependency-name: "pdfjs-dist"
- dependency-name: "@types/*"
@@ -33,6 +30,18 @@ updates:
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/artifact-append"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/artifact-extract"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/artifact-initialize"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/before-install"
schedule:
@@ -41,6 +50,10 @@ updates:
directory: "/.github/actions/download-node-modules-and-artifacts"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/e2e"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/enable-dryrun"
schedule:
@@ -53,6 +66,10 @@ updates:
directory: "/.github/actions/npm-check-bundle"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/print-affected-libs"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/set-npm-tag"
schedule:
@@ -61,6 +78,14 @@ updates:
directory: "/.github/actions/setup"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/setup-chrome"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/slack-group-area"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github/actions/upload-node-modules-and-artifacts"
schedule:
-23
View File
@@ -1,23 +0,0 @@
---
applyTo: "**/*.html"
---
# HTML Development Standards
* Simple Templates: Keep templates as simple as possible, avoiding complex logic directly in the template. Delegate complex logic to the component's TypeScript code.
* Native Control Flow: Use the new built-in control flow syntax (`@if`, `@for`, `@switch`) instead of the older structural directives (`*ngIf`, `*ngFor`, `*ngSwitch`).
* NgOptimizedImage: Use `NgOptimizedImage` for all static images to automatically optimize image loading and performance.
* Async Pipe: Use the `async` pipe to handle observables in templates. This automatically subscribes and unsubscribes, preventing memory leaks.
* Prefer pipes over functions in templates for performance reasons, as pipes are only re-evaluated when their inputs change.
## Accessibility Standards
* Add `alt` text to all images
* Label form inputs with `<mat-label>` or `aria-label`
* Ensure interactive elements have accessible names
* Add `role`, `aria-labelledby`, and `aria-describedby` when semantic HTML isn't sufficient
* All interactive elements must be keyboard accessible
* Ensure 4.5:1 contrast ratio for normal text, 3:1 for large text
* Use `aria-live="polite"` for status updates
* Watch out for misused/non-semantic elements (e.g., <div> instead of <section>)
* Avoid broken heading hierarchy (e.g., h1 → h3 without h2)
-19
View File
@@ -1,19 +0,0 @@
---
applyTo: "**/*.scss"
---
# SCSS Development Standards
* Avoid using `!important` to override styles unless absolutely necessary; instead, increase specificity or refactor the code structure.
* Avoid using Angular Material internal class; prefer using Angular Material Design 3 theming and tokens.
* Use variables for colors, fonts, and other design tokens to maintain consistency across the project.
* Use mixins for reusable styles and to avoid code duplication.
* Ensure that styles are responsive and work well across different screen sizes and devices.
* Use CSS Grid and Flexbox for layout to create flexible and responsive designs.
* Avoid using overly specific selectors (e.g., #header .nav ul li a)
* Be DRY (avoid repeated styles for similar elements)
* Check for inconsistent naming (e.g., mixing BEM and arbitrary classes)
* Make sure selected colors have right contrast (satisfy WCAG AA)
* All interactive elements should have focus state
* Avoid disabled outline without alternative focus indicators
-96
View File
@@ -1,96 +0,0 @@
---
applyTo: "**/*.ts"
---
# TypeScript Development Standards
## Type Safety
* Strict Type Checking: Always enable and adhere to strict type checking. This helps catch errors early and improves code quality.
* Prefer Type Inference: Allow TypeScript to infer types when they are obvious from the context. This reduces verbosity while maintaining type safety.
* Avoid `any`: Do not use the `any` type unless absolutely necessary as it bypasses type checking. Prefer `unknown` when a type is uncertain and you need to handle it safely.
* Use strict null checks (no `null` or `undefined` without explicit handling)
* Use type guards and union types for robust type checking
* Check for missing return types in function signatures
* Avoid implicit `any` (untyped function parameters)
## Naming Conventions
* Use PascalCase for types, interfaces, and classes
* Use camelCase for variables, functions, and methods
* Use UPPER_CASE for constants
## Modern TypeScript Patterns
* Use optional chaining (`?.`) and nullish coalescing (`??`)
* Prefer `const` over `let`; never use `var`
* Use arrow functions for callbacks and short functions
* Avoid enums - they generate additional code at compile time, which increases the size of the final file. This can have a negative impact on the loading speed and performance of the app. Prefer union types or literal types instead.
* Avoid unhandled promise rejections (missing .catch() or try/catch)
* Use proper async/await pattern
* Avoid inefficient array operations (e.g., nested .map())
* Use destructuring for object/array access
* Prefer arrow functions
## Angular Best Practices
* Standalone Components: Always use standalone components, directives, and pipes. Avoid using `NgModules` for new features or refactoring existing ones.
* Implicit Standalone: When creating standalone components, you do not need to explicitly set `standalone: true` inside the `@Component`, `@Directive` and `@Pipe` decorators, as it is implied by default.
* Lazy Loading: Implement lazy loading for feature routes to improve initial load times of your application.
* Use Angular Material or other modern UI libraries for consistent styling and UI components.
* Implement proper error handling with RxJS operators (e.g., catchError)
* Verify if newly added functionalities can utilize Angular Signals for fine-grained reactivity, reducing change detection overhead.
* Utilize AOT (Ahead-of-Time) compilation and tree-shaking for efficient, smaller bundle sizes.
* Prefer class binding over `ngClass` and `ngStyle` for better performance.
* Use protected on class members that are only used by a component's template, as it allows for better encapsulation while still being accessible to the template.
* Use readonly for properties that shouldn't change.
* Use `takeUntilDestroyed` & `destroyRef`: The `takeUntilDestroyed` and `destroyRef` have been introduced with Angular 16 and help to reduce boilerplate code related to unsubscribing on the `OnDestroy` hook.
* Organize the order of properties and methods in Angular components for readability and maintainability. Recommended order is:
1. **Injected services** Whether they are public or private, it's clear they are dependencies of the class.
2. **Inputs**: Properties that receive data from outside.
3. **Outputs**: Events that the component can trigger.
4. **ViewChild/ContentChild**: References to HTML elements.
5. **Public static properties**: Constants and static members that are accessible to everyone.
6. **Readonly properties**: Immutable public properties.
7. **Public properties**: Data and functions available to everyone.
8. **Private static properties**: Constants and static members that are only accessible within the class.
9. **Private readonly properties**: Immutable private properties.
10. **Private properties**: Data and functions used only inside the component.
11. **Setters and Getters**: Methods for accessing and modifying properties.
12. **Constructor**: Used to initialize the component.
13. **Lifecycle Hooks**: Methods that run at specific times in the components lifecycle.
14. **Public methods**: Functions available to everyone.
15. **Private methods**: Functions used only inside the component.
## Components
* Single Responsibility: Keep components small, focused, and responsible for a single piece of functionality.
* Reactive Forms: Prefer Reactive forms over Template-driven forms for complex forms, validation, and dynamic controls due to their explicit, immutable, and synchronous nature.
* Use Typed Forms: Typed Forms in Angular are a new feature introduced in Angular 14 that provide stronger type checking for reactive forms. They allow developers to define the structure and types of form controls, making it easier to catch errors at compile-time rather than runtime.
## Services
* Single Responsibility: Design services around a single, well-defined responsibility.
* `providedIn: 'root'`: Use the `providedIn: 'root'` option when declaring injectable services to ensure they are singletons and tree-shakable.
* `inject()` Function: Prefer the `inject()` function over constructor injection when injecting dependencies, especially within `provide` functions, `computed` properties, or outside of constructor context.
## Unit testing
* Write unit tests for components, services, and pipes using Jasmine and Karma.
* Test cases should be reasonably groupped based on tested functionality/behaviour using describe blocks.
* Use plain English test names based on the should <expectedBehavior> when <stateUnderTest> pattern as a guideline.
* Use Angular's TestBed for component testing with mocked dependencies
* Avoid Direct Calls to Component Lifecycle Hooks: Instead of directly invoking lifecycle hooks like `ngOnInit()`, use Angular's testing utilities to trigger them naturally. For example, use `fixture.detectChanges()` to trigger change detection, which will automatically call `ngOnInit()` and other lifecycle hooks in the correct order.
* Use fixture.componentRef.setInput() Instead of Direct Input Assignment: When testing components with inputs, use `fixture.componentRef.setInput()` to set input values. This method ensures that Angular's change detection is properly triggered, allowing the component to react to input changes as it would in a real application.
* Use the Provide Mock Store for testing components that rely on NgRx state management. This allows you to mock the store and control the state during tests without needing to set up a full NgRx environment.
* Mock HTTP requests using provideHttpClientTesting
* Import only the minimal required modules
* Avoid NO_ERRORS_SCHEMA and CUSTOM_ELEMENTS_SCHEMA in tests to ensure proper error detection
* Do not verify mocked methods
* Avoid mocking component methods unless necessary; prefer testing actual behavior
* Avoid testing private methods directly; test them through public methods instead
* Avoid testing methods or behaviours of children components; use shallow testing or mock child components instead
* Use the overrideProviders API to replace components, directives, pipes, or services declared deep within the module hierarchy
* Avoid async/await in synchronous unit tests
* Prefer data-automation-id over CSS class when possible
* Do not use toBeDefined() to check if an element is visible.
-42
View File
@@ -1,42 +0,0 @@
name: "Build Lib Workflow"
on:
workflow_call:
inputs:
base_ref:
description: 'Base branch for affected calculation'
required: false
type: string
default: 'develop'
env:
NODE_OPTIONS: "--max-old-space-size=5120"
jobs:
build:
name: "Build affected libs"
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Setup environment
id: setup-env
uses: ./.github/actions/setup
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
- name: Save nx cache
if: ${{ success() }}
uses: ./.github/actions/save-nx-cache
with:
cache-suffix: build
+4 -4
View File
@@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
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@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0
# 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@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0
# ️ 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@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0
+87
View File
@@ -0,0 +1,87 @@
name: "cron e2e daily"
on:
workflow_dispatch:
schedule:
- cron: '0 12 * * 1-5' #At 12:00 on every day-of-week from Monday through Friday.
env:
BASE_REF: ${{ github.base_ref }}
HEAD_REF: ${{ github.head_ref }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GITHUB_BRANCH: ${{ github.ref_name }}
GH_BUILD_DIR: ${{ github.workspace }}
GH_COMMIT: ${{ github.sha }}
BUILD_ID: ${{ github.run_id }}
GH_RUN_NUMBER: ${{ github.run_attempt }}
GH_BUILD_NUMBER: ${{ github.run_id }}
JOB_ID: ${{ github.run_id }}
PROXY_HOST_BPM: ${{ secrets.E2E_HOST }}
E2E_HOST_APA: ${{ secrets.E2E_HOST_APA }}
E2E_IDENTITY_HOST_APA: ${{ secrets.E2E_IDENTITY_HOST_APA }}
E2E_HOST: ${{ secrets.E2E_HOST }}
E2E_USERNAME: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }}
E2E_ADMIN_EMAIL_IDENTITY: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_ADMIN_PASSWORD_IDENTITY: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
USERNAME_ADF: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
PASSWORD_ADF: ${{ secrets.E2E_PASSWORD }}
URL_HOST_ADF: "http://localhost:4200"
IDENTITY_ADMIN_EMAIL: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
IDENTITY_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
AWS_S3_BUCKET_ACTIVITI_LICENSE: ${{ secrets.AWS_S3_BUCKET_ACTIVITI_LICENSE }}
HOST_SSO: ${{ secrets.HOST_SSO }}
LOG_LEVEL: "ERROR"
E2E_LOG_LEVEL: "ERROR"
E2E_MODELER_USERNAME: ${{ secrets.E2E_MODELER_USERNAME }}
E2E_MODELER_PASSWORD: ${{ secrets.E2E_MODELER_PASSWORD }}
EXTERNAL_ACS_HOST: ${{ secrets.EXTERNAL_ACS_HOST }}
E2E_DEVOPS_USERNAME: ${{ secrets.E2E_DEVOPS_USERNAME }}
E2E_DEVOPS_PASSWORD: ${{ secrets.E2E_DEVOPS_PASSWORD }}
USERNAME_SUPER_ADMIN_ADF: ${{ secrets.USERNAME_SUPER_ADMIN_ADF }}
PASSWORD_SUPER_ADMIN_ADF: ${{ secrets.PASSWORD_SUPER_ADMIN_ADF }}
HR_USER: ${{ secrets.HR_USER }}
HR_USER_PASSWORD: ${{ secrets.HR_USER_PASSWORD }}
SMART_RUNNER_PATH: ".protractor-smartrunner"
S3_DBP_PATH: ${{ secrets.S3_DBP_PATH }}
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
NODE_OPTIONS: "--max-old-space-size=5120"
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
DOCKER_REPOSITORY: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/demo-shell"
REPO_OWNER: "Alfresco"
REPO_NAME: "alfresco-ng2-components"
DEMO_SHELL_DIR: "./dist/demo-shell"
STORYBOOK_DIR: "./dist/storybook/stories"
BUILT_LIBS_DIR: "./dist/libs"
NODE_MODULES_DIR: "./node_modules"
SMART_RUNNER_DIRECTORY: ".protractor-smartrunner"
SAVE_SCREENSHOT: true
REDIRECT_URI: /
BROWSER_RUN: false
MAXINSTANCES: 2
PLAYWRIGHT_WORKERS: 2
PLAYWRIGHT_STORYBOOK_E2E_HOST: http://localhost
PLAYWRIGHT_STORYBOOK_E2E_PORT: 4400
PROXY_HOST_ECM: ${{ secrets.E2E_HOST }}
jobs:
init-artifact:
runs-on: ubuntu-latest
name: Initialize artifacts
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: ./.github/actions/artifact-initialize
with:
artifact-name: global-e2e-result
file-name: e2e-failures.txt
run-e2e:
name: run e2e
uses: ./.github/workflows/pull-request.yml
with:
cron-run: true
secrets: inherit
+112
View File
@@ -0,0 +1,112 @@
name: "git-tag"
on:
workflow_call:
inputs:
dry-run-flag:
description: 'enable dry-run on artifact push'
required: false
type: boolean
default: true
push:
branches:
- master
- master-patch-*
env:
BASE_REF: ${{ github.base_ref }}
HEAD_REF: ${{ github.head_ref }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GITHUB_BRANCH: ${{ github.ref_name }}
GH_BUILD_DIR: ${{ github.workspace }}
GH_COMMIT: ${{ github.sha }}
BUILD_ID: ${{ github.run_id }}
GH_RUN_NUMBER: ${{ github.run_attempt }}
GH_BUILD_NUMBER: ${{ github.run_id }}
JOB_ID: ${{ github.run_id }}
PROXY_HOST_BPM: ${{ secrets.E2E_HOST }}
E2E_IDENTITY_HOST_APA: ${{ secrets.E2E_IDENTITY_HOST_APA }}
E2E_HOST_APA: ${{ secrets.E2E_HOST_APA }}
E2E_HOST: ${{ secrets.E2E_HOST }}
E2E_USERNAME: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }}
E2E_ADMIN_EMAIL_IDENTITY: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_ADMIN_PASSWORD_IDENTITY: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
USERNAME_ADF: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
PASSWORD_ADF: ${{ secrets.E2E_PASSWORD }}
URL_HOST_ADF: "http://localhost:4200"
IDENTITY_ADMIN_EMAIL: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
IDENTITY_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
AWS_S3_BUCKET_ACTIVITI_LICENSE: ${{ secrets.AWS_S3_BUCKET_ACTIVITI_LICENSE }}
HOST_SSO: ${{ secrets.HOST_SSO }}
LOG_LEVEL: "ERROR"
E2E_LOG_LEVEL: "ERROR"
E2E_MODELER_USERNAME: ${{ secrets.E2E_MODELER_USERNAME }}
E2E_MODELER_PASSWORD: ${{ secrets.E2E_MODELER_PASSWORD }}
EXTERNAL_ACS_HOST: ${{ secrets.EXTERNAL_ACS_HOST }}
E2E_DEVOPS_USERNAME: ${{ secrets.E2E_DEVOPS_USERNAME }}
E2E_DEVOPS_PASSWORD: ${{ secrets.E2E_DEVOPS_PASSWORD }}
USERNAME_SUPER_ADMIN_ADF: ${{ secrets.USERNAME_SUPER_ADMIN_ADF }}
PASSWORD_SUPER_ADMIN_ADF: ${{ secrets.PASSWORD_SUPER_ADMIN_ADF }}
HR_USER: ${{ secrets.HR_USER }}
HR_USER_PASSWORD: ${{ secrets.HR_USER_PASSWORD }}
SMART_RUNNER_PATH: ".protractor-smartrunner"
S3_DBP_PATH: ${{ secrets.S3_DBP_PATH }}
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
NODE_OPTIONS: "--max-old-space-size=5120"
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
DOCKER_REPOSITORY: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/demo-shell"
NPM_REGISTRY_ADDRESS: ${{ secrets.NPM_REGISTRY_ADDRESS }}
NPM_REGISTRY_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }}
BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
REPO_OWNER: "Alfresco"
REPO_NAME: "alfresco-ng2-components"
DEMO_SHELL_DIR: "./dist/demo-shell"
STORYBOOK_DIR: "./dist/storybook/stories"
BUILT_LIBS_DIR: "./dist/libs"
NODE_MODULES_DIR: "./node_modules"
SMART_RUNNER_DIRECTORY: ".protractor-smartrunner"
SAVE_SCREENSHOT: true
REDIRECT_URI: /
BROWSER_RUN: false
MAXINSTANCES: 2
PLAYWRIGHT_WORKERS: 2
PLAYWRIGHT_STORYBOOK_E2E_HOST: http://localhost
PLAYWRIGHT_STORYBOOK_E2E_PORT: 4400
PROXY_HOST_ECM: ${{ secrets.E2E_HOST }}
jobs:
release:
uses: ./.github/workflows/release.yml
secrets: inherit
with:
dry-run-flag: false
setup:
needs: release
timeout-minutes: 20
name: "Release tag"
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: $BOT_GITHUB_TOKEN
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- id: set-dryrun
uses: ./.github/actions/enable-dryrun
with:
dry-run-flag: ${{ inputs.dry-run-flag }}
- name: install NPM
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version-file: '.nvmrc'
- name: "Release tag"
run: |
git fetch --all --quiet
BRANCH=${GITHUB_REF##*/} ./scripts/github/release/git-tag.sh ${{ steps.set-dryrun.outputs.dryrun }}
+3 -6
View File
@@ -9,17 +9,14 @@ jobs:
if: github.event.registry_package.package_type == 'npm' && github.event.registry_package.name == 'adf-core'
steps:
- name: Dispatch event to monorepo
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PACKAGE_NAME: ${{ github.event.registry_package.name }}
PACKAGE_VERSION: ${{ github.event.registry_package.package_version.name }}
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ secrets.PAT_WRITE_PKG }}
retries: 3
script: |
const payload = {
package_name: process.env.PACKAGE_NAME,
package_version: process.env.PACKAGE_VERSION
package_name: "${{ github.event.registry_package.name }}",
package_version: "${{ github.event.registry_package.package_version.name }}"
};
await github.rest.repos.createDispatchEvent({
-33
View File
@@ -1,33 +0,0 @@
name: Pull Translations from Crowdin
on:
schedule:
- cron: "0 7-17 * * 1-5"
workflow_dispatch:
workflow_call:
secrets:
BOT_GITHUB_TOKEN:
required: true
CROWDIN_TRANSLATIONS_TOKEN:
required: true
jobs:
pull-from-crowdin:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: develop
token: ${{ secrets.BOT_GITHUB_TOKEN }}
- name: Pull translations from Crowdin
uses: crowdin/github-action@8868a33591d21088edfc398968173a3b98d51706 # v2.16.2
with:
skip_ref_checkout: true
upload_sources: false
download_translations: true
create_pull_request: true
localization_branch_name: automated-translations-update
pull_request_title: "GH auto: Automated Update of Translations from Crowdin"
pull_request_base_branch_name: develop
env:
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
CROWDIN_TOKEN: ${{ secrets.CROWDIN_TRANSLATIONS_TOKEN }}
+414 -217
View File
@@ -4,17 +4,17 @@ on:
workflow_call:
inputs:
dry-run-flag:
description: "enable dry-run on artifact push"
description: 'enable dry-run on artifact push'
required: false
type: boolean
default: true
devel:
description: "devel"
description: 'devel'
required: false
type: boolean
default: false
cron-run:
description: "disables jobs which should not run when cron runs e2es"
description: 'disables jobs which should not run when cron runs e2es'
required: false
type: boolean
default: false
@@ -31,20 +31,77 @@ concurrency:
cancel-in-progress: true
env:
BASE_REF: ${{ github.base_ref }}
HEAD_REF: ${{ github.head_ref }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GITHUB_BRANCH: ${{ github.ref_name }}
GH_BUILD_DIR: ${{ github.workspace }}
GH_COMMIT: ${{ github.sha }}
BUILD_ID: ${{ github.run_id }}
GH_RUN_NUMBER: ${{ github.run_attempt }}
GH_BUILD_NUMBER: ${{ github.run_id }}
JOB_ID: ${{ github.run_id }}
PROXY_HOST_BPM: ${{ secrets.E2E_HOST }}
E2E_IDENTITY_HOST_APA: ${{ secrets.E2E_IDENTITY_HOST_APA }}
E2E_HOST_APA: ${{ secrets.E2E_HOST_APA }}
E2E_HOST: ${{ secrets.E2E_HOST }}
E2E_USERNAME: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }}
E2E_ADMIN_EMAIL_IDENTITY: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_ADMIN_PASSWORD_IDENTITY: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
USERNAME_ADF: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
PASSWORD_ADF: ${{ secrets.E2E_PASSWORD }}
URL_HOST_ADF: "http://localhost:4200"
IDENTITY_ADMIN_EMAIL: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
IDENTITY_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
AWS_S3_BUCKET_ACTIVITI_LICENSE: ${{ secrets.AWS_S3_BUCKET_ACTIVITI_LICENSE }}
HOST_SSO: ${{ secrets.HOST_SSO }}
LOG_LEVEL: "ERROR"
E2E_LOG_LEVEL: "ERROR"
E2E_MODELER_USERNAME: ${{ secrets.E2E_MODELER_USERNAME }}
E2E_MODELER_PASSWORD: ${{ secrets.E2E_MODELER_PASSWORD }}
EXTERNAL_ACS_HOST: ${{ secrets.EXTERNAL_ACS_HOST }}
E2E_DEVOPS_USERNAME: ${{ secrets.E2E_DEVOPS_USERNAME }}
E2E_DEVOPS_PASSWORD: ${{ secrets.E2E_DEVOPS_PASSWORD }}
USERNAME_SUPER_ADMIN_ADF: ${{ secrets.USERNAME_SUPER_ADMIN_ADF }}
PASSWORD_SUPER_ADMIN_ADF: ${{ secrets.PASSWORD_SUPER_ADMIN_ADF }}
HR_USER: ${{ secrets.HR_USER }}
HR_USER_PASSWORD: ${{ secrets.HR_USER_PASSWORD }}
SMART_RUNNER_PATH: ".protractor-smartrunner"
S3_DBP_PATH: ${{ secrets.S3_DBP_PATH }}
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
NODE_OPTIONS: "--max-old-space-size=5120"
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
DOCKER_REPOSITORY: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/demo-shell"
REPO_OWNER: "Alfresco"
REPO_NAME: "alfresco-ng2-components"
DEMO_SHELL_DIR: "./dist/demo-shell"
STORYBOOK_DIR: "./dist/storybook/stories"
BUILT_LIBS_DIR: "./dist/libs"
NODE_MODULES_DIR: "./node_modules"
SMART_RUNNER_DIRECTORY: ".protractor-smartrunner"
SAVE_SCREENSHOT: true
REDIRECT_URI: /
BROWSER_RUN: false
MAXINSTANCES: 2
PLAYWRIGHT_WORKERS: 2
PLAYWRIGHT_STORYBOOK_E2E_HOST: http://localhost
PLAYWRIGHT_STORYBOOK_E2E_PORT: 4400
PROXY_HOST_ECM: ${{ secrets.E2E_HOST }}
jobs:
pre-checks:
runs-on: ubuntu-latest
outputs:
code-changed: ${{ steps.path-filter.outputs.code-changed }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Ensure SHA pinned actions
uses: hyland/github-actions-ensure-sha-pinned-actions@7d494b6b53e71f75194d74b4dd890a2266d060b5 # v2.0.0
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@b88cd0aad2c36a63e42c71f81cb1958fed95ac87 # v3.0.10
- name: Check package-lock.json version
run: |
@@ -55,273 +112,413 @@ jobs:
exit 1
fi
- name: Detect code changes
id: path-filter
env:
GH_TOKEN: ${{ github.token }}
run: |
if [ "${{ github.event_name }}" != "pull_request" ]; then
echo "Not a PR event — assuming code changed"
echo "code-changed=true" >> $GITHUB_OUTPUT
exit 0
fi
FILES=$(gh api /repos/$GITHUB_REPOSITORY/pulls/${{ github.event.pull_request.number }}/files --paginate --jq '.[].filename')
CODE_CHANGED="false"
while IFS= read -r file; do
case "$file" in
*.md|docs/*|.github/*.md|.github/CODEOWNERS|.github/dependabot.yml|LICENSE*|NOTICE*|.editorconfig|.gitattributes)
;;
*)
CODE_CHANGED="true"
break
;;
esac
done <<< "$FILES"
echo "code-changed=$CODE_CHANGED" >> $GITHUB_OUTPUT
echo "Code changed: $CODE_CHANGED"
check-if-pr-is-approved:
runs-on: ubuntu-latest
steps:
- name: Check if PR approval can be skipped
id: skip-check
env:
EVENT_NAME: ${{ github.event_name }}
ACTOR: ${{ github.actor }}
DEVEL_FLAG: ${{ inputs.devel }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message || github.event.pull_request.title || '' }}
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- name: Get branch name
uses: Alfresco/alfresco-build-tools/.github/actions/get-branch-name@76affd16a4dd9a440cf66bf42656b1d826e8dfc2 # v5.34.0
- name: Save commit message
uses: Alfresco/alfresco-build-tools/.github/actions/get-commit-message@76affd16a4dd9a440cf66bf42656b1d826e8dfc2 # v5.34.0
- name: ci:force flag parser
shell: bash
run: |
skip="false"
if [ "$EVENT_NAME" == "schedule" ] || [ "$EVENT_NAME" == "workflow_dispatch" ]; then
echo -e "\033[32mSchedule/dispatch event — skipping approval check\033[0m"
skip="true"
elif [ "$ACTOR" == "dependabot[bot]" ] || [ "$ACTOR" == "alfresco-build" ]; then
echo -e "\033[32mCommit by $ACTOR — skipping approval check\033[0m"
skip="true"
if [ "${{ github.event_name }}" == "schedule" ] || [ "${{ github.actor }}" == "dependabot[bot]" ]; then
echo -e "\033[32mci:force check can be skipped\033[0m"
skip_check="true"
elif [[ "$COMMIT_MESSAGE" == *"[ci:force]"* ]]; then
echo -e "\033[32m[ci:force] flag detected — skipping approval check\033[0m"
skip="true"
elif [[ "$DEVEL_FLAG" == "true" ]]; then
echo -e "\033[32mDevel flag — skipping approval check\033[0m"
skip="true"
echo -e "\033[32m[ci:force] flag detected. No need for approval.\033[0m"
skip_check="true"
fi
echo "skip=$skip" >> $GITHUB_OUTPUT
- name: Get PR number
if: ${{ steps.skip-check.outputs.skip != 'true' }}
id: pr-number
env:
GH_TOKEN: ${{ github.token }}
run: |
PR_NUMBER="${{ github.event.pull_request.number }}"
if [ -z "$PR_NUMBER" ]; then
PR_NUMBER=$(gh pr view --json number --jq '.number' 2>/dev/null || echo "")
fi
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "PR: $PR_NUMBER"
if: ${{ github.event_name != 'schedule' && github.event_name != 'workflow_dispatch' }}
uses: kamatama41/get-pr-number-action@0bcaab5752c0b699149e74667c8ce2f764cbb7fa # v0.9.1
id: action
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Check if PR is approved
if: ${{ steps.skip-check.outputs.skip != 'true' && steps.pr-number.outputs.pr_number != '' }}
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ steps.pr-number.outputs.pr_number }}
- name: show pr number
shell: bash
run: |
echo "Checking approval for PR: $PR_NUMBER"
checkApproval=$(gh api /repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews | jq '.[] | select(.state == "APPROVED") | .user.login')
if [[ $checkApproval ]]; then
echo -e "\033[32mPR approved\033[0m"
else
echo -e "\033[31mPR NOT approved\033[0m"
exit 1
echo "PR: ${{ steps.action.outputs.number }}"
- name: check if pr is approved
env:
DEVEL_FLAG: ${{ inputs.devel }}
GH_TOKEN: ${{ github.token }}
skip_check: "false"
run: |
if [ "${{ github.event_name }}" == "schedule" ] || [ "${{ github.actor }}" == "dependabot[bot]" ]; then
echo -e "\033[32mci:force check can be skipped\033[0m"
skip_check="true"
elif [[ "$COMMIT_MESSAGE" == *"[ci:force]"* ]]; then
echo -e "\033[32m[ci:force] flag detected. No need for approval.\033[0m"
skip_check="true"
fi
if [ "${{ github.actor }}" == "dependabot[bot]" ] || [ "${{ github.actor }}" == "alfresco-build" ]; then
echo -e "\033[32mCommit by ${{ github.actor }}. No need for approval.\033[0m"
skip_check="true"
fi
if [ "${{ github.event_name }}" == "schedule" ] || [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo -e "\033[32mSchedule event\033[0m"
skip_check="true"
fi
if [[ "$DEVEL_FLAG" == "true" ]]; then
echo -e "\033[32mDevel flag\033[0m"
skip_check="true"
fi
if [ "$skip_check" == "false" ]; then
echo "Checking PR approval"
prNumber=${{ steps.action.outputs.number }}
echo "PR: $prNumber"
checkApproval=$(gh api /repos/$GITHUB_REPOSITORY/pulls/$prNumber/reviews | jq '.[] | select(.state == "APPROVED") | .user.login')
if [[ $checkApproval ]]; then
echo -e "\033[32mPR approved\033[0m"
else
echo -e "\033[31mPR NOT approved\033[0m"
exit 1
fi
fi
setup:
# long timeout required when cache has to be recreated
timeout-minutes: 30
name: "Setup"
runs-on: ubuntu-latest
needs: [check-if-pr-is-approved, pre-checks]
if: ${{ needs.pre-checks.outputs.code-changed == 'true' }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- name: Setup environment
id: setup-env
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
fetch-depth: 0 # Fetch all history for all tags and branches
- uses: ./.github/actions/setup
- name: install
run: |
npm run bundle:js-api
npm run bundle:cli
- name: Save nx cache
if: ${{ success() }}
uses: ./.github/actions/save-nx-cache
npm ci
npx nx run js-api:bundle
npx nx run cli:bundle
npx nx run testing:bundle
- run: npx nx print-affected $NX_CALCULATION_FLAGS
- uses: ./.github/actions/upload-node-modules-and-artifacts
unit-tests:
timeout-minutes: 30
name: "Unit tests: ${{ matrix.unit-tests.name }}"
runs-on: ubuntu-latest
needs: [setup]
strategy:
fail-fast: false
# max-parallel: 4
matrix:
unit-tests:
- name: js-api
exclude: "core,insights,content-services,process-services,process-services-cloud,eslint-plugin-eslint-angular"
- name: content-services
exclude: "insights,core,extensions,process-services,process-services-cloud,eslint-plugin-eslint-angular,js-api"
- name: core
exclude: "insights,content-services,process-services,process-services-cloud,eslint-plugin-eslint-angular,js-api"
- name: insights
exclude: "core,extensions,content-services,process-services-cloud,process-services,eslint-plugin-eslint-angular,js-api"
- name: process-services
exclude: "core,extensions,content-services,process-services-cloud,insights,eslint-plugin-eslint-angular,js-api"
- name: process-cloud
exclude: "insights,core,extensions,content-services,process-services,eslint-plugin-eslint-angular,js-api"
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
cache-suffix: setup
fetch-depth: 0 # Fetch all history for all tags and branches
- uses: ./.github/actions/setup
- uses: ./.github/actions/download-node-modules-and-artifacts
- name: Run unit tests
run: |
/usr/bin/xvfb-run --auto-servernum npx nx affected:test $NX_CALCULATION_FLAGS --exclude=${{ matrix.unit-tests.exclude }}
lint:
# long timeout required when cache has to be recreated
timeout-minutes: 30
name: "Lint"
runs-on: ubuntu-latest
needs: [setup]
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- name: Setup environment
id: setup-env
uses: ./.github/actions/setup
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
- name: Save nx cache
if: ${{ success() }}
uses: ./.github/actions/save-nx-cache
with:
cache-suffix: lint
fetch-depth: 0 # Fetch all history for all tags and branches
- uses: ./.github/actions/setup
- uses: ./.github/actions/download-node-modules-and-artifacts
- run: npx nx affected --target=lint $NX_CALCULATION_FLAGS
trigger-build:
name: "Build Libs"
needs: [setup]
uses: ./.github/workflows/build-lib-workflow.yml
with:
base_ref: ${{ github.base_ref || 'develop' }}
build-storybook:
build-libs:
# long timeout required when cache has to be recreated
timeout-minutes: 30
name: "Build Storybook"
name: "Build libs"
runs-on: ubuntu-latest
needs: [setup]
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0 # Fetch all history for all tags and branches
- uses: ./.github/actions/setup
- uses: ./.github/actions/download-node-modules-and-artifacts
- run: npx nx affected:build $NX_CALCULATION_FLAGS --prod
- run: npx nx build demoshell --configuration production
- run: npx nx affected --target=build-storybook $NX_CALCULATION_FLAGS --configuration=ci --parallel=1
- uses: ./.github/actions/upload-node-modules-and-artifacts
e2e-storybook:
timeout-minutes: 20
name: "e2e: storybook"
needs: [build-libs, lint, unit-tests]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- name: Setup environment
id: setup-env
uses: ./.github/actions/setup
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' }}
fetch-depth: 0 # Fetch all history for all
- uses: ./.github/actions/setup
- uses: ./.github/actions/download-node-modules-and-artifacts
- name: Process Cloud Storybook Playwright
run: |
npx 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
with:
cache-suffix: storybook
npx playwright install chromium
sudo sysctl -w fs.inotify.max_user_watches=524288
npx nx affected --target=e2e-playwright $NX_CALCULATION_FLAGS || exit 1
- uses: ./.github/actions/upload-node-modules-and-artifacts
trigger-unit-tests:
name: "Unit Tests"
needs: [setup]
uses: ./.github/workflows/unit-test-workflow.yml
with:
base_ref: ${{ github.base_ref || 'develop' }}
PR-size-check:
if: ${{ github.event_name == 'pull_request' }}
e2e:
timeout-minutes: 90
name: "e2e: ${{ matrix.e2e-test.description }}"
needs: [build-libs, lint, unit-tests]
runs-on: ubuntu-latest
strategy:
fail-fast: false
# max-parallel: 4
matrix:
e2e-test:
- description: "Core"
test-id: "core"
artifact-id: "core"
folder: "core"
provider: "ALL"
auth: "OAUTH"
check-cs-env: "true"
check-ps-env: "true"
deps: "testing"
- description: "Content: Components"
test-id: "content-services"
artifact-id: "content-services-components"
folder: "content-services/components"
provider: "ECM"
auth: "BASIC"
check-cs-env: "true"
deps: "testing"
- description: "Content: Directives"
test-id: "content-services"
artifact-id: "content-services-directives"
folder: "content-services/directives"
provider: "ECM"
auth: "BASIC"
check-cs-env: "true"
deps: "testing"
- description: "Content: Document List"
test-id: "content-services"
artifact-id: "content-services-document-list"
folder: "content-services/document-list"
provider: "ECM"
auth: "BASIC"
check-cs-env: "true"
deps: "testing"
- description: "Content: Metadata"
test-id: "content-services"
artifact-id: "content-services-metadata"
folder: "content-services/metadata"
provider: "ECM"
auth: "BASIC"
check-cs-env: "true"
deps: "testing"
- description: "Content: Upload and Versioning"
test-id: "content-services"
artifact-id: "content-services-upload"
folder: "content-services/upload"
provider: "ECM"
auth: "BASIC"
check-cs-env: "true"
deps: "testing"
- description: "Search"
test-id: "content-services"
artifact-id: "content-services-search"
folder: "search"
provider: "ECM"
auth: "BASIC"
check-cs-env: "true"
deps: "testing"
- description: "Process: Form"
test-id: "process-services"
artifact-id: "process-services-form"
folder: "process-services/form"
provider: "BPM"
auth: "OAUTH"
check-ps-env: "true"
check-external-cs-env: "true"
deps: "testing"
- description: "Process: Process"
test-id: "process-services"
artifact-id: "process-services-process"
folder: "process-services/process"
provider: "BPM"
auth: "OAUTH"
check-ps-env: "true"
check-external-cs-env: "true"
deps: "testing"
- description: "Process: Tasks"
test-id: "process-services"
artifact-id: "process-services-tasks"
folder: "process-services/tasks"
provider: "BPM"
auth: "OAUTH"
check-ps-env: "true"
check-external-cs-env: "true"
deps: "testing"
- description: "Process: Widget"
test-id: "process-services"
artifact-id: "process-services-widgets"
folder: "process-services/widgets"
provider: "BPM"
auth: "OAUTH"
check-ps-env: "true"
check-external-cs-env: "true"
deps: "testing"
- description: "Process Cloud: Form"
test-id: "process-services-cloud"
artifact-id: "process-services-cloud-form-field"
folder: "process-services-cloud/form-field"
provider: "ALL"
auth: "OAUTH"
apa-proxy: true
check-cs-env: "true"
check-ps-cloud-env: "true"
deps: "testing"
- description: "Process Cloud: Process"
test-id: "process-services-cloud"
artifact-id: "process-services-cloud-process"
folder: "process-services-cloud/process"
provider: "ALL"
auth: "OAUTH"
apa-proxy: true
check-cs-env: "true"
check-ps-cloud-env: "true"
deps: "testing"
- description: "Process Cloud: Start Task"
test-id: "process-services-cloud"
artifact-id: "process-services-cloud-start-task"
folder: "process-services-cloud/start-task"
provider: "ALL"
auth: "OAUTH"
apa-proxy: true
check-cs-env: "true"
check-ps-cloud-env: "true"
deps: "testing"
- description: "Process Cloud: Tasks List"
test-id: "process-services-cloud"
artifact-id: "process-services-cloud-task-list"
folder: "process-services-cloud/task-list"
provider: "ALL"
auth: "OAUTH"
apa-proxy: true
check-cs-env: "true"
check-ps-cloud-env: "true"
deps: "testing"
steps:
- name: Check PR size
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
script: |
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
const additions = pr.additions;
const deletions = pr.deletions;
const totalChanges = additions + deletions;
const changedFiles = pr.changed_files;
let size = 'S';
if (totalChanges > 1000 || changedFiles > 30) size = 'XL';
else if (totalChanges > 500 || changedFiles > 20) size = 'L';
else if (totalChanges > 200 || changedFiles > 10) size = 'M';
core.summary
.addHeading('PR Size: ' + size, 3)
.addTable([
[{data: 'Metric', header: true}, {data: 'Count', header: true}],
['Files changed', String(changedFiles)],
['Additions', '+' + String(additions)],
['Deletions', '-' + String(deletions)],
['Total changes', String(totalChanges)],
]);
if (size === 'XL') {
core.summary.addRaw('⚠️ This PR is very large. Consider splitting it into smaller PRs for easier review.');
}
await core.summary.write();
if (size === 'XL') {
core.warning('This PR has ' + totalChanges + ' changes across ' + changedFiles + ' files. Consider splitting it for easier review.');
}
fetch-depth: 0 # Fetch all history for all
- uses: ./.github/actions/setup
with:
enable-cache: "true"
enable-node-modules-cache: "true"
- uses: ./.github/actions/download-node-modules-and-artifacts
- name: e2e
uses: ./.github/actions/e2e
with:
e2e-test-id: ${{ matrix.e2e-test.test-id }}
e2e-test-folder: ${{ matrix.e2e-test.folder }}
e2e-artifact-id: ${{matrix.e2e-test.artifact-id}}
e2e-test-provider: ${{ matrix.e2e-test.provider }}
e2e-test-auth: ${{ matrix.e2e-test.auth }}
check-cs-env: ${{ matrix.e2e-test.check-cs-env }}
check-ps-env: ${{ matrix.e2e-test.check-ps-env }}
check-ps-cloud-env: ${{ matrix.e2e-test.check-ps-cloud-env }}
check-external-cs-env: ${{ matrix.e2e-test.check-external-cs-env }}
apa-proxy: ${{ matrix.e2e-test.apa-proxy }}
deps: ${{ matrix.e2e-test.deps }}
PR-forbidden-labels:
if: ${{ inputs.cron-run == '' || inputs.cron-run == 'false' }}
runs-on: ubuntu-latest
steps:
- name: Check for forbidden labels
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- id: checkoutRepo
name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 1
- name: PR contains forbidden labels
id: pr-forbidden
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const { data: issue } = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const labels = issue.labels?.map(item => item.name) || [];
const forbidden = ['next version ➡️', 'do not merge🙅🏻‍♂️'];
const issueHasLabels = require('./scripts/github/update/check-issue-has-label.js');
const checkLabels = ['next version ➡️', 'do not merge🙅🏻‍♂️'];
if (forbidden.some(l => labels.includes(l))) {
const hasLabel = await issueHasLabels({github, context, checkLabels})
if(hasLabel) {
core.setFailed('The PR contains a forbidden label! You are not allowed to merge until the label is there.');
}
- name: Check value after
run: |
echo "result ${{ toJson(steps.pr-forbidden.*.result) }}" && echo "result ${{ steps.pr-forbidden.*.result }}"
echo "result ${{ contains(toJson(steps.pr-forbidden.*.result), 'failure') }}"
finalize:
if: ${{ always() }}
runs-on: ubuntu-latest
name: Final Results
needs:
[
check-if-pr-is-approved,
pre-checks,
setup,
trigger-unit-tests,
lint,
trigger-build,
build-storybook,
PR-forbidden-labels,
]
needs: [check-if-pr-is-approved, pre-checks, setup, unit-tests, lint, build-libs, e2e, e2e-storybook]
steps:
- name: Check job execution status
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: identify-slack-group
id: groups
if: ${{ github.event_name == 'schedule' }}
uses: ./.github/actions/slack-group-area
with:
affected: ${{ steps.e2e-result.outputs.result }}
- uses: slackapi/slack-github-action@70cd7be8e40a46e8b0eced40b0de447bdb42f68e # v1.26.0
name: Nofify QA failure
if: ${{ github.event_name == 'schedule' && contains(needs.*.result, 'failure') }}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
with:
channel-id: 'C016SMNNL8L' #guild-channel
slack-message: "🔴 Warning: The daily ADF cronjob failed\nWorkflow run : <https://github.com/Alfresco/alfresco-ng2-components/actions/runs/${{ github.run_id }}| here>\nDetails: ${{ steps.e2e-result.outputs.result }}\nArea: ${{ steps.groups.outputs.groups}}"
- name: workflow failure
run: exit 1
if: ${{ contains(needs.*.result, 'failure') }}
- name: workflow canceled
run: exit 1
if: ${{ contains(needs.*.result, 'cancelled') }}
- name: workflow success
run: exit 0
if: ${{ contains(needs.*.result, 'success') }}
+1 -1
View File
@@ -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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- name: Automatic Rebase
+158
View File
@@ -0,0 +1,158 @@
name: Release lib on branch
run-name: Release lib on branch ${{ github.ref_name }}
on:
workflow_dispatch:
inputs:
dry-run-flag:
description: 'enable dry-run on artifact push'
required: false
type: boolean
default: true
env:
BASE_REF: ${{ github.base_ref }}
HEAD_REF: ${{ github.head_ref }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GITHUB_BRANCH: ${{ github.ref_name }}
GH_BUILD_DIR: ${{ github.workspace }}
GH_COMMIT: ${{ github.sha }}
BUILD_ID: ${{ github.run_id }}
GH_RUN_NUMBER: ${{ github.run_attempt }}
GH_BUILD_NUMBER: ${{ github.run_id }}
JOB_ID: ${{ github.run_id }}
PROXY_HOST_BPM: ${{ secrets.E2E_HOST }}
E2E_IDENTITY_HOST_APA: ${{ secrets.E2E_IDENTITY_HOST_APA }}
E2E_HOST_APA: ${{ secrets.E2E_HOST_APA }}
E2E_HOST: ${{ secrets.E2E_HOST }}
E2E_USERNAME: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }}
E2E_ADMIN_EMAIL_IDENTITY: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_ADMIN_PASSWORD_IDENTITY: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
#USERNAME_ADF: ${{ secrets.E2E_USERNAME }}
USERNAME_ADF: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
PASSWORD_ADF: ${{ secrets.E2E_PASSWORD }}
URL_HOST_ADF: "http://localhost:4200"
IDENTITY_ADMIN_EMAIL: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
IDENTITY_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
AWS_S3_BUCKET_ACTIVITI_LICENSE: ${{ secrets.AWS_S3_BUCKET_ACTIVITI_LICENSE }}
HOST_SSO: ${{ secrets.HOST_SSO }}
LOG_LEVEL: "ERROR"
E2E_LOG_LEVEL: "ERROR"
E2E_MODELER_USERNAME: ${{ secrets.E2E_MODELER_USERNAME }}
E2E_MODELER_PASSWORD: ${{ secrets.E2E_MODELER_PASSWORD }}
EXTERNAL_ACS_HOST: ${{ secrets.EXTERNAL_ACS_HOST }}
E2E_DEVOPS_USERNAME: ${{ secrets.E2E_DEVOPS_USERNAME }}
E2E_DEVOPS_PASSWORD: ${{ secrets.E2E_DEVOPS_PASSWORD }}
USERNAME_SUPER_ADMIN_ADF: ${{ secrets.USERNAME_SUPER_ADMIN_ADF }}
PASSWORD_SUPER_ADMIN_ADF: ${{ secrets.PASSWORD_SUPER_ADMIN_ADF }}
HR_USER: ${{ secrets.HR_USER }}
HR_USER_PASSWORD: ${{ secrets.HR_USER_PASSWORD }}
SMART_RUNNER_PATH: ".protractor-smartrunner"
S3_DBP_PATH: ${{ secrets.S3_DBP_PATH }}
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
NODE_OPTIONS: "--max-old-space-size=5120"
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
DOCKER_REPOSITORY: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/demo-shell"
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
REPO_OWNER: "Alfresco"
REPO_NAME: "alfresco-ng2-components"
DEMO_SHELL_DIR: "./dist/demo-shell"
STORYBOOK_DIR: "./dist/storybook/stories"
BUILT_LIBS_DIR: "./dist/libs"
NODE_MODULES_DIR: "./node_modules"
SMART_RUNNER_DIRECTORY: ".protractor-smartrunner"
SAVE_SCREENSHOT: true
REDIRECT_URI: /
BROWSER_RUN: false
MAXINSTANCES: 2
PLAYWRIGHT_WORKERS: 2
PLAYWRIGHT_STORYBOOK_E2E_HOST: http://localhost
PLAYWRIGHT_STORYBOOK_E2E_PORT: 4400
jobs:
setup:
timeout-minutes: 20
name: "Setup"
runs-on: ubuntu-latest
steps:
- name: set TAG_NPM BRANCH
shell: bash
run: |
TAG_NPM="branch"
echo "Set TAG with name: ${TAG_NPM}"
echo "TAG_NPM=${TAG_NPM}" >> $GITHUB_ENV
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- uses: ./.github/actions/setup
with:
enable-cache: false
enable-node-modules-cache: false
- name: install
run: |
npm ci
npx nx run js-api:bundle
npx nx run cli:bundle
npx nx run testing:bundle
- uses: ./.github/actions/upload-node-modules-and-artifacts
release-npm:
needs: [setup]
timeout-minutes: 30
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- uses: ./.github/actions/setup
with:
enable-cache: false
enable-node-modules-cache: false
- id: set-dryrun
uses: ./.github/actions/enable-dryrun
with:
dry-run-flag: ${{ inputs.dry-run-flag }}
- uses: ./.github/actions/download-node-modules-and-artifacts
- name: build libraries
run: |
set -u;
./scripts/update-version.sh -gnu || exit 1;
npx nx affected:build $NX_CALCULATION_FLAGS --prod --exclude="demoshell" --skip-nx-cache
npx nx affected $NX_CALCULATION_FLAGS --target=pretheme
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
name: release libraries GH registry
with:
node-version-file: '.nvmrc'
registry-url: 'https://npm.pkg.github.com'
scope: '@alfresco'
- run: npx nx affected --target=npm-publish $NX_CALCULATION_FLAGS --tag=branch|| exit 1
env:
NODE_AUTH_TOKEN: ${{ secrets.PAT_WRITE_PKG }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
name: release libraries Npm registry
with:
node-version-file: '.nvmrc'
registry-url: 'https://${{ vars.NPM_REGISTRY_ADDRESS }}'
scope: '@alfresco'
- run: npx nx affected --target=npm-publish $NX_CALCULATION_FLAGS --tag=branch || exit 1
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }}
npm-check-bundle:
needs: [release-npm]
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: ./.github/actions/npm-check-bundle
+162 -97
View File
@@ -8,26 +8,13 @@ on:
required: false
type: boolean
default: true
workflow_dispatch:
inputs:
dry-run-flag:
description: 'enable dry-run on artifact push'
required: false
type: boolean
default: true
pull_request:
types: [closed]
branches:
- develop
- develop-patch*
push:
branches:
- master
- master-patch-*
permissions:
id-token: write # Required for OIDC
contents: read
- develop-patch*
- master-patch*
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -36,154 +23,232 @@ concurrency:
env:
BASE_REF: ${{ github.base_ref }}
HEAD_REF: ${{ github.head_ref }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GITHUB_BRANCH: ${{ github.ref_name }}
GH_BUILD_DIR: ${{ github.workspace }}
GH_COMMIT: ${{ github.sha }}
BUILD_ID: ${{ github.run_id }}
GH_RUN_NUMBER: ${{ github.run_attempt }}
GH_BUILD_NUMBER: ${{ github.run_id }}
JOB_ID: ${{ github.run_id }}
PROXY_HOST_BPM: ${{ secrets.E2E_HOST }}
E2E_IDENTITY_HOST_APA: ${{ secrets.E2E_IDENTITY_HOST_APA }}
E2E_HOST_APA: ${{ secrets.E2E_HOST_APA }}
E2E_HOST: ${{ secrets.E2E_HOST }}
E2E_USERNAME: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }}
E2E_ADMIN_EMAIL_IDENTITY: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
E2E_ADMIN_PASSWORD_IDENTITY: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
#USERNAME_ADF: ${{ secrets.E2E_USERNAME }}
USERNAME_ADF: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
PASSWORD_ADF: ${{ secrets.E2E_PASSWORD }}
URL_HOST_ADF: "http://localhost:4200"
IDENTITY_ADMIN_EMAIL: ${{ secrets.E2E_ADMIN_EMAIL_IDENTITY }}
IDENTITY_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD_IDENTITY }}
AWS_S3_BUCKET_ACTIVITI_LICENSE: ${{ secrets.AWS_S3_BUCKET_ACTIVITI_LICENSE }}
HOST_SSO: ${{ secrets.HOST_SSO }}
LOG_LEVEL: "ERROR"
E2E_LOG_LEVEL: "ERROR"
E2E_MODELER_USERNAME: ${{ secrets.E2E_MODELER_USERNAME }}
E2E_MODELER_PASSWORD: ${{ secrets.E2E_MODELER_PASSWORD }}
EXTERNAL_ACS_HOST: ${{ secrets.EXTERNAL_ACS_HOST }}
E2E_DEVOPS_USERNAME: ${{ secrets.E2E_DEVOPS_USERNAME }}
E2E_DEVOPS_PASSWORD: ${{ secrets.E2E_DEVOPS_PASSWORD }}
USERNAME_SUPER_ADMIN_ADF: ${{ secrets.USERNAME_SUPER_ADMIN_ADF }}
PASSWORD_SUPER_ADMIN_ADF: ${{ secrets.PASSWORD_SUPER_ADMIN_ADF }}
HR_USER: ${{ secrets.HR_USER }}
HR_USER_PASSWORD: ${{ secrets.HR_USER_PASSWORD }}
SMART_RUNNER_PATH: ".protractor-smartrunner"
S3_DBP_PATH: ${{ secrets.S3_DBP_PATH }}
S3_BUILD_BUCKET_SHORT_NAME: ${{ secrets.S3_BUILD_BUCKET_SHORT_NAME }}
NODE_OPTIONS: "--max-old-space-size=5120"
DOCKER_REPOSITORY_DOMAIN: ${{ secrets.DOCKER_REPOSITORY_DOMAIN }}
DOCKER_REPOSITORY_USER: ${{ secrets.DOCKER_REPOSITORY_USER }}
DOCKER_REPOSITORY_PASSWORD: ${{ secrets.DOCKER_REPOSITORY_PASSWORD }}
DOCKER_REPOSITORY_STORYBOOK: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/storybook"
DOCKER_REPOSITORY: "${{ secrets.DOCKER_REPOSITORY_DOMAIN }}/alfresco/demo-shell"
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
REPO_OWNER: "Alfresco"
REPO_NAME: "alfresco-ng2-components"
DEMO_SHELL_DIR: "./dist/demo-shell"
STORYBOOK_DIR: "./dist/storybook/stories"
BUILT_LIBS_DIR: "./dist/libs"
NODE_MODULES_DIR: "./node_modules"
SMART_RUNNER_DIRECTORY: ".protractor-smartrunner"
SAVE_SCREENSHOT: true
REDIRECT_URI: /
BROWSER_RUN: false
MAXINSTANCES: 2
PLAYWRIGHT_WORKERS: 2
PLAYWRIGHT_STORYBOOK_E2E_HOST: http://localhost
PLAYWRIGHT_STORYBOOK_E2E_PORT: 4400
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.pull_request.merged == true || github.ref_name == 'master' || github.ref_name == 'master-patch-*'
name: "Setup"
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- uses: ./.github/actions/setup
with:
enable-cache: false
enable-node-modules-cache: false
- name: install
run: |
npm ci
npm run bundle:js-api
npm run bundle:cli
npx nx run js-api:bundle
npx nx run cli:bundle
npx nx run testing:bundle
- uses: ./.github/actions/upload-node-modules-and-artifacts
release-demoshell:
needs: [setup]
timeout-minutes: 15
if: github.event.pull_request.merged == true || github.ref_name == 'master' || github.ref_name == 'master-patch-*'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 1
- run: git fetch --all
- id: set-dryrun
uses: ./.github/actions/enable-dryrun
with:
dry-run-flag: ${{ inputs.dry-run-flag }}
- uses: ./.github/actions/setup
with:
enable-cache: false
enable-node-modules-cache: false
- uses: ./.github/actions/download-node-modules-and-artifacts
- name: release Demoshell docker
run: |
npx nx build demoshell --configuration production
. ./scripts/github/release/docker-tag.sh
./scripts/github/release/release-demoshell-docker.sh ${{ steps.set-dryrun.outputs.dryrun }}
release-storybook:
needs: [setup]
timeout-minutes: 15
if: github.event.pull_request.merged == true || github.ref_name == 'master' || github.ref_name == 'master-patch-*'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 1
- run: git fetch --all
- id: set-dryrun
uses: ./.github/actions/enable-dryrun
with:
dry-run-flag: ${{ inputs.dry-run-flag }}
- uses: ./.github/actions/setup
with:
enable-cache: false
enable-node-modules-cache: false
act: ${{ inputs.dry-run-flag }}
- uses: ./.github/actions/download-node-modules-and-artifacts
- name: release Storybook docker
run: |
npx nx run stories:build-storybook --configuration ci
. ./scripts/github/release/docker-tag.sh
./scripts/github/release/release-storybook-docker.sh ${{ steps.set-dryrun.outputs.dryrun }}
release-npm:
needs: [setup]
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.pull_request.merged == true || github.ref_name == 'master' || github.ref_name == 'master-patch-*'
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- id: setup
uses: ./.github/actions/setup
- uses: ./.github/actions/setup
with:
enable-cache: false
enable-node-modules-cache: false
- id: set-dryrun
uses: ./.github/actions/enable-dryrun
with:
dry-run-flag: ${{ inputs.dry-run-flag }}
- uses: ./.github/actions/download-node-modules-and-artifacts
- name: Set libraries versions
id: set-version
- name: build libraries
run: |
set -u;
./scripts/github/build/bumpversion.sh
- name: Set migrations
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const setMigrations = require('./scripts/github/release/set-migrations.js');
setMigrations();
- name: build libraries
run: |
npm run build:libs
npm run build:schematics
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
npx nx affected:build $NX_CALCULATION_FLAGS --prod --exclude="demoshell" --skip-nx-cache
npx nx affected $NX_CALCULATION_FLAGS --target=pretheme
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
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: npx nx affected --target=npm-publish $NX_CALCULATION_FLAGS --tag=$TAG_NPM || exit 1
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
NODE_AUTH_TOKEN: ${{ secrets.PAT_WRITE_PKG }}
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
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: npx nx affected --target=npm-publish $NX_CALCULATION_FLAGS --tag=$TAG_NPM || exit 1
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_REGISTRY_TOKEN }}
create-git-tag:
propagate:
needs: [release-npm]
if: ${{ contains(toJson(github.event.pull_request.labels.*.name), 'hxp-upstream') }}
runs-on: ubuntu-latest
needs: [setup, release-npm]
if: github.event_name != 'workflow_dispatch'
name: Create github tag
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: HxP upstream invoke
uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 # v4.0.0
with:
fetch-depth: 1
- uses: './.github/actions/create-git-tag'
with:
tagName: ${{ needs.release-npm.outputs.release_version }}
repo: Alfresco/hxp-frontend-apps
ref: develop
workflow: upstream-adf.yml
token: ${{ secrets.ALFRESCO_BUILD_GH_TOKEN }}
wait-for-completion: false
inputs: >
{
"tag_version": "alpha"
}
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.pull_request.merged == true || github.ref_name == 'master' || github.ref_name == 'master-patch-*'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: ./.github/actions/npm-check-bundle
push-translation-keys-to-crowdin:
name: Push translations keys to Crowdin
if: github.ref_name == 'develop' && github.event_name != 'workflow_dispatch'
runs-on: ubuntu-latest
needs: [setup]
permissions:
contents: read
packages: read
actions: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Push Source Files to Crowdin
uses: crowdin/github-action@8868a33591d21088edfc398968173a3b98d51706 # v2.16.2
with:
upload_sources: true
upload_sources_args: --delete-obsolete
env:
CROWDIN_TOKEN: ${{ secrets.CROWDIN_TRANSLATIONS_TOKEN }}
pull-translation-keys-from-crowdin:
needs: push-translation-keys-to-crowdin
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 }}
CROWDIN_TRANSLATIONS_TOKEN: ${{ secrets.CROWDIN_TRANSLATIONS_TOKEN }}
finalize:
if: always()
runs-on: ubuntu-latest
name: Final Results
needs: [release-npm, npm-check-bundle]
steps:
- name: Check job execution status
if: >-
${{
contains(needs.*.result, 'failure')
|| contains(needs.*.result, 'cancelled')
}}
run: exit 1
if: always()
runs-on: ubuntu-latest
name: Final Results
needs: [release-demoshell, release-storybook, release-npm, npm-check-bundle]
steps:
- uses: slackapi/slack-github-action@70cd7be8e40a46e8b0eced40b0de447bdb42f68e # v1.26.0
name: Nofify FE eng-guild-front-end workflow failed
if: ${{ contains(toJson(needs.*.result), 'failure') }}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
with:
channel-id: 'C016SMNNL8L' #eng-guild-front-end
slack-message: "🔴 Warning: The release workflow of alfresco-ng2-components pipe failed\n Author username: ${{ github.event.pull_request.user.login }}\n Workflow run <https://github.com/Alfresco/alfresco-ng2-components/actions/runs/${{ github.run_id }}| here>\n>"
-87
View File
@@ -1,87 +0,0 @@
name: "Unit Tests Workflow"
on:
workflow_call:
inputs:
base_ref:
description: 'Base branch for affected calculation'
required: false
type: string
default: 'develop'
jobs:
generate-affected-matrix:
name: "Generate affected matrix"
runs-on: ubuntu-latest
outputs:
unitMatrix: ${{ steps.set-matrix.outputs.unitMatrix }}
hasProjects: ${{ steps.set-matrix.outputs.hasProjects }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Setup environment
id: setup-env
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)
echo "Affected projects for UNIT: $AFFECTED_UNIT"
if [ -z "$AFFECTED_UNIT" ]; then
echo "No affected projects found"
echo "hasProjects=false" >> $GITHUB_OUTPUT
echo "unitMatrix=[]" >> $GITHUB_OUTPUT
else
UNIT_MATRIX_JSON=$(echo "$AFFECTED_UNIT" | xargs -n1 | jq -R -s -c 'split("\n") | map(select(length > 0)) | map({ "project": . })')
echo "Matrix UNIT: $UNIT_MATRIX_JSON"
echo "hasProjects=true" >> $GITHUB_OUTPUT
echo "unitMatrix=$UNIT_MATRIX_JSON" >> $GITHUB_OUTPUT
fi
- name: Save nx cache
if: ${{ success() }}
uses: ./.github/actions/save-nx-cache
with:
cache-suffix: test-matrix
unit-tests:
timeout-minutes: 30
runs-on: ubuntu-latest
needs: generate-affected-matrix
if: ${{ needs.generate-affected-matrix.outputs.hasProjects == 'true' }}
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.generate-affected-matrix.outputs.unitMatrix) }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- 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
- name: Save nx cache
if: ${{ success() }}
uses: ./.github/actions/save-nx-cache
with:
cache-suffix: test-${{ matrix.project }}
+17 -5
View File
@@ -1,26 +1,38 @@
/.angular/cache
*.log
node_modules
bundles
workspace.xml
.idea/
*.iml
.env.*
.env
dist
!./.github/actions/**/dist
e2e/.env.cloud
tmp
temp
/nxcache
e2e-output*/
/e2e/downloads/
*.npmrc
.history
/ng2-components/ng2-alfresco-core/prebuilt-themes/
.ng_pkg_build/
/demo-shell/dist-dev-temp/
/lib/export-new.json
/lib/config/exportCheck.js
/docs/sourceinfo
/docs/docs.json
/protractorFailuresReport
coverage/
/desktop.ini
out-tsc
!/.protractor-smartrunner/
/reports/
e2e-result-*
licenses.txt
.DS_Store
desktop.ini
.angular
NX
.nx
nxcache
.husky
.cursor/rules/nx-rules.mdc
.github/instructions/nx.instructions.md
+2 -1
View File
@@ -1,4 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
export NODE_OPTIONS=--max_old_space_size=8192
lint-staged
npx lint-staged
+1 -1
View File
@@ -1 +1 @@
24.14.0
18.20.3
+9
View File
@@ -0,0 +1,9 @@
import { create } from '@storybook/theming';
import alfrescoLogo from '../lib/core/src/lib/assets/images/alfresco-logo.svg';
export default create({
base: 'light',
brandTitle: 'Hyland | Alfresco Storybook App',
brandUrl: 'https://www.alfresco.com/',
brandImage: alfrescoLogo,
});
+10
View File
@@ -0,0 +1,10 @@
module.exports = {
framework: {
name: '@storybook/angular',
options: {}
},
staticDirs: [{ from: '../../../demo-shell/src/app.config.json', to: 'app.config.json' }],
docs: {},
stories: [],
addons: ['@chromatic-com/storybook']
};
-24
View File
@@ -1,24 +0,0 @@
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import type { StorybookConfig } from '@storybook/angular';
const config: StorybookConfig = {
framework: {
name: getAbsolutePath('@storybook/angular'),
options: {}
},
staticDirs: [],
stories: ['lib/**/*.stories.ts'],
features: {
backgrounds: false
},
core: {
disableTelemetry: true
}
};
export default config;
function getAbsolutePath(value: string): any {
return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)));
}
+5
View File
@@ -0,0 +1,5 @@
export const parameters = {
docs: { inlineStories: true },
controls: { expanded: true }
};
export const tags = ['autodocs'];
-11
View File
@@ -1,11 +0,0 @@
import type { Preview } from '@storybook/angular';
const preview: Preview = {
parameters: {
docs: { inlineStories: true },
controls: { expanded: true }
},
tags: ['autodocs']
};
export default preview;
-6
View File
@@ -1,12 +1,6 @@
{
"extends": ["stylelint-config-standard-scss"],
"rules": {
"custom-property-pattern": [
"^(mat-sys|mdc)",
{
"message": "Use only Angular Material system variables (--mat-sys-*, --mdc-*)"
}
],
"color-function-notation": "legacy",
"alpha-value-notation": "number",
"color-no-invalid-hex": true,
+17
View File
@@ -0,0 +1,17 @@
specFile=$1;
configFile=$2;
findconfig() {
if [ -f "$1" ]; then
printf '%s\n' "${PWD%/}/$1"
elif [ "$PWD" = / ]; then
false
else
(cd .. && findconfig $1)
fi
}
DIR=$(dirname "$specFile")
cd $DIR
configFile=`findconfig "$configFile"`;
echo "$configFile";
+5
View File
@@ -0,0 +1,5 @@
{
"recommendations": [
"lucono.karma-test-explorer"
]
}
+27
View File
@@ -0,0 +1,27 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "e2e",
"program": "${workspaceFolder}/node_modules/protractor/bin/protractor",
"args": [
"./e2e/protractor.conf.js",
"--specs=${file}"
],
"envFile": "${workspaceFolder}/.env",
"console": "integratedTerminal",
"sourceMaps": true,
"smartStep": true,
"skipFiles": [
"${workspaceFolder}/node_modules/**/*.js",
"<node_internals>/**/*.js"
],
"internalConsoleOptions": "neverOpen"
}
]
}
+5 -2
View File
@@ -5,6 +5,7 @@
"**/.svn": true,
"**/.hg": true,
"**/.DS_Store": true,
"**/coverage": true,
"**/.happypack": true
},
"markdownlint.config": {
@@ -20,8 +21,10 @@
"stylelint.enable": true,
"css.validate": false,
"scss.validate": false,
"stylelint.configFile": "${workspaceFolder}/.stylelintrc.json",
"stylelint.configBasedir": "${workspaceFolder}",
"stylelint.config": {
"extends": "./.stylelintrc.json"
},
"stylelint.configFile": ".stylelintrc.json",
"stylelint.validate": [
"css",
"less",
View File
+3 -1
View File
@@ -103,6 +103,8 @@ Create a file next to the project's `package.json`, call it `proxy.conf.json` an
Note that if you are running the App, Content Service or Process Service on different ports, you should change the ports accordingly in your local configuration.
For further details about how to configure a webpack proxy please refer to the [official documentation](https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md).
## Configure nginx proxy
@@ -149,7 +151,7 @@ See the [Alfresco community page](https://community.alfresco.com/community/appli
If you want to completely enable CORS calls in your Content Services and Process Services,
please refer to the following Alfresco documents:
* [Enable Cross Origin Resource Sharing (CORS) in Alfresco Process Services](https://support.hyland.com/r/Alfresco/Alfresco-Process-Services/24.4/Alfresco-Process-Services/Configure/Overview/CORS)
* [Enable Cross Origin Resource Sharing (CORS) in Alfresco Process Services](http://docs.alfresco.com/process-services1.6/topics/enabling-cors.html)
* Enable Cross Origin Resource Sharing (CORS) in Alfresco Content Services
+28
View File
@@ -0,0 +1,28 @@
# 1. Generate licenses
FROM node:18.16-alpine3.17 AS builder
WORKDIR /usr/src/alfresco
COPY package.json package.json
# 2. Generate image
FROM nginxinc/nginx-unprivileged:1.21-alpine
USER root
RUN apk update && apk upgrade
USER 101
ARG PROJECT_NAME
COPY docker/default.conf.template /etc/nginx/templates/
COPY docker/docker-entrypoint.d/* /docker-entrypoint.d/
COPY dist/$PROJECT_NAME /usr/share/nginx/html/
COPY dist/$PROJECT_NAME/app.config.json /etc/nginx/templates/app.config.json.template
USER root
RUN chmod a+w -R /etc/nginx/conf.d
USER 101
ENV BASE_PATH=/
ENV NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx/conf.d
+18 -8
View File
@@ -13,7 +13,7 @@ Node: 18.x
NPM: 9.x
```
Also, check out the tutorial: [Creating your first ADF Application](docs/tutorials/creating-your-first-adf-application.md)
Also, check out the tutorial [Creating your first ADF Application](docs/tutorials/creating-your-first-adf-application.md)
for full details on what you may need to install before using ADF.
### See also
@@ -28,7 +28,6 @@ You can find the sources for all ADF components in the [`lib`](/lib) folder.
## Libraries
ADF Libraries list:
- [Content services](https://github.com/Alfresco/alfresco-ng2-components/tree/develop/lib/content-services)
- [Core](https://github.com/Alfresco/alfresco-ng2-components/tree/develop/lib/core)
- [Extensions](https://github.com/Alfresco/alfresco-ng2-components/tree/develop/lib/extensions)
@@ -37,13 +36,24 @@ ADF Libraries list:
- [Process service](https://github.com/Alfresco/alfresco-ng2-components/tree/develop/lib/process-services)
- [Stories](https://github.com/Alfresco/alfresco-ng2-components/tree/develop/lib/stories)
## Demo Application
A separate application showcasing integration of components can be found
[here](https://github.com/Alfresco/alfresco-ng2-components/tree/master/demo-shell).
The app has examples of basic interaction for both APS and ACS components.
## Application generator for Yeoman
To speed up the development, you can use the
[Generator for Yeoman](https://github.com/Alfresco/generator-ng2-alfresco-app).
## Browser Support
All components are supported in the following browsers:
| **Browser** | **Version** |
|-------------|-------------|
| Chrome | Latest |
| Safari | Latest |
| Firefox | Latest |
| Edge | Latest |
|**Browser** |**Version** |
|--- |--- |
| Chrome | Latest |
| Safari | Latest |
| Firefox | Latest |
| Edge | Latest |
-13
View File
@@ -1,13 +0,0 @@
"project_id": "11"
"api_token_env": "CROWDIN_TOKEN"
"base_path": "."
"base_url": "https://hyland.api.crowdin.com"
"preserve_hierarchy": true
"files": [
{
"source": "/**/**/i18n/en.json",
"translation": "/%original_path%/%two_letters_code%.%file_extension%",
"export_only_approved": "true",
"update_option": "update_without_changes"
}
]
+6 -8
View File
@@ -14,11 +14,9 @@
"backend",
"baseitem",
"BASESHAREURL",
"berseria",
"booleanvisibility",
"booleanvisibilityprocess",
"boolitem",
"BPMECM",
"BPMHOST",
"cardview",
"checkboxes",
@@ -49,7 +47,6 @@
"dryrun",
"ECMBPM",
"ECMHOST",
"edjs",
"Examinate",
"exif",
"filedata",
@@ -89,6 +86,7 @@
"mouseenter",
"multiselect",
"mysites",
"nginx",
"numbervisibilityprocess",
"OAUTHCONFIG",
"oidc",
@@ -123,7 +121,6 @@
"Theming",
"transcluded",
"transclusion",
"triggerable",
"truthy",
"typeahead",
"typeahed",
@@ -137,7 +134,6 @@
"uploader",
"uploadfileform",
"userinfo",
"validatable",
"validators",
"waypoint",
"waypoints",
@@ -145,8 +141,11 @@
"Whitespaces",
"xdescribe",
"xsrf",
"BPMECM",
"berseria",
"zestiria",
"webscript"
"validatable",
"edjs"
],
"dictionaries": [
"html",
@@ -157,8 +156,7 @@
],
"ignorePaths": [
"lib/{content-services,core,extensions,insights,process-services}/**/*.spec.ts",
"lib/{content-services,core,extensions,insights,process-services}/**/*.mock.ts",
"**/*.stories.ts"
"lib/{content-services,core,extensions,insights,process-services}/**/*.mock.ts"
],
"ignoreRegExpList": [
"(\"|'|`)((?:\\\\1|(?:(?!\\1).))*)\\1"
+93
View File
@@ -0,0 +1,93 @@
/**
* This file decorates the Angular CLI with the Nx CLI to enable features such as computation caching
* and faster execution of tasks.
*
* It does this by:
*
* - Patching the Angular CLI to warn you in case you accidentally use the undecorated ng command.
* - Symlinking the ng to nx command, so all commands run through the Nx CLI
* - Updating the package.json postinstall script to give you control over this script
*
* The Nx CLI decorates the Angular CLI, so the Nx CLI is fully compatible with it.
* Every command you run should work the same when using the Nx CLI, except faster.
*
* Because of symlinking you can still type `ng build/test/lint` in the terminal. The ng command, in this case,
* will point to nx, which will perform optimizations before invoking ng. So the Angular CLI is always invoked.
* The Nx CLI simply does some optimizations before invoking the Angular CLI.
*
* To opt out of this patch:
* - Replace occurrences of nx with ng in your package.json
* - Remove the script from your postinstall script in your package.json
* - Delete and reinstall your node_modules
*/
const fs = require('fs');
const os = require('os');
const cp = require('child_process');
const isWindows = os.platform() === 'win32';
const output = require('nx/src/utils/output').output;
/**
* Paths to files being patched
*/
const angularCLIInitPath = 'node_modules/@angular/cli/lib/cli/index.js';
/**
* Patch index.js to warn you if you invoke the undecorated Angular CLI.
*/
function patchAngularCLI(initPath) {
const angularCLIInit = fs.readFileSync(initPath, 'utf-8').toString();
if (!angularCLIInit.includes('NX_CLI_SET')) {
fs.writeFileSync(
initPath,
`
if (!process.env['NX_CLI_SET']) {
const { output } = require('@nx/workspace');
output.warn({ title: 'The Angular CLI was invoked instead of the Nx CLI. Use "npx ng [command]" or "nx [command]" instead.' });
}
${angularCLIInit}
`
);
}
}
/**
* Symlink of ng to nx, so you can keep using `ng build/test/lint` and still
* invoke the Nx CLI and get the benefits of computation caching.
*/
function symlinkNgCLItoNxCLI() {
try {
const ngPath = './node_modules/.bin/ng';
const nxPath = './node_modules/.bin/nx';
if (isWindows) {
/**
* This is the most reliable way to create symlink-like behavior on Windows.
* Such that it works in all shells and works with npx.
*/
['', '.cmd', '.ps1'].forEach((ext) => {
fs.writeFileSync(ngPath + ext, fs.readFileSync(nxPath + ext));
});
} else {
// If unix-based, symlink
cp.execSync(`ln -sf ./nx ${ngPath}`);
}
} catch (e) {
output.error({
title: 'Unable to create a symlink from the Angular CLI to the Nx CLI:' + e.message
});
throw e;
}
}
try {
symlinkNgCLItoNxCLI();
patchAngularCLI(angularCLIInitPath);
output.log({
title: 'Angular CLI has been decorated to enable computation caching.'
});
} catch (e) {
output.error({
title: 'Decoration of the Angular CLI did not complete successfully'
});
}
+73
View File
@@ -0,0 +1,73 @@
path = require('path');
module.exports = {
extends: '../.eslintrc.js',
ignorePatterns: ['!**/*'],
overrides: [
{
files: ['*.ts'],
parserOptions: {
project: [
path.join(__dirname, 'tsconfig.app.json'),
path.join(__dirname, 'src/tsconfig.spec.json'),
path.join(__dirname, 'e2e/tsconfig.e2e.json')
],
createDefaultProgram: true
},
plugins: ['eslint-plugin-unicorn', 'eslint-plugin-rxjs'],
rules: {
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: ['adf', 'app'],
style: 'kebab-case'
}
],
'@angular-eslint/directive-selector': [
'error',
{
type: ['element', 'attribute'],
prefix: ['adf', 'app'],
style: 'kebab-case'
}
],
'@angular-eslint/no-host-metadata-property': 'off',
'@angular-eslint/no-input-prefix': 'error',
'@typescript-eslint/consistent-type-definitions': 'error',
'@typescript-eslint/dot-notation': 'off',
'@typescript-eslint/explicit-member-accessibility': [
'off',
{
accessibility: 'explicit'
}
],
'@typescript-eslint/no-inferrable-types': 'off',
'@typescript-eslint/no-require-imports': 'off',
'@typescript-eslint/no-var-requires': 'error',
'comma-dangle': 'error',
'default-case': 'error',
'import/order': 'off',
'max-len': [
'error',
{
code: 240
}
],
'no-bitwise': 'off',
'no-duplicate-imports': 'error',
'no-multiple-empty-lines': 'error',
'no-redeclare': 'error',
'no-return-await': 'error',
'rxjs/no-create': 'error',
'rxjs/no-subject-unsubscribe': 'error',
'rxjs/no-subject-value': 'error',
'rxjs/no-unsafe-takeuntil': 'error',
'unicorn/filename-case': 'error'
}
},
{
files: ['*.html'],
rules: {}
}
]
};
+59
View File
@@ -0,0 +1,59 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
testem.log
/typings
# e2e
/e2e/*.js
/e2e/*.map
# System Files
.DS_Store
Thumbs.db
typings/
node_modules/
bower_components/
lib/
app/**/*.js
app/**/*.js.map
app/**/*.d.ts
!app/js/Polyline.js
.idea
dist/
coverage/
!/e2e/protractor.conf.js
+75
View File
@@ -0,0 +1,75 @@
# ADF Demo Application
Please note that this application is not an official product, but a testing and demo application to showcase complex interactions of ADF components.
## Installing
To correctly use this demo check that on your machine you have [Node](https://nodejs.org/en/) version 5.x.x or higher.
```sh
git clone https://github.com/Alfresco/alfresco-ng2-components.git
cd alfresco-ng2-components
npm install
npm start
```
## Proxy settings and CORS
To simplify development and reduce the time to get the application started, we have the following Proxy settings:
- **http://localhost:3000/ecm** is mapped to **http://localhost:8080**
- **http://localhost:3000/bpm** is mapped to **http://localhost:9999**
The settings above address most common scenarios for running ACS on port 8080 and APS on port 9999 and allow you to skip the CORS configuration.
If you would like to change default proxy settings, please edit the `proxy.conf.js` file.
## Application settings (server-side)
All server-side application settings are stored in the [src/app.config.json](src/app.config.json).
By default the configuration files have the content similar to the following one:
```json
{
"$schema": "../../lib/core/app-config/schema.json",
"ecmHost": "http://{hostname}:{port}",
"bpmHost": "http://{hostname}:{port}",
"application": {
"name": "Alfresco ADF Application"
}
}
```
## Development build
```sh
npm start
```
This command compiles and starts the project in watch mode.
Browser will automatically reload upon changes.
Upon start, you can navigate to `http://localhost:3000` with your preferred browser.
### Important notes
This script is recommended for development environment and not suited for headless servers and network access.
## Production build
```sh
npm run build
npm run start:prod
```
This command builds project in `production` mode.
All output is placed to `dist` folder and can be served to your preferred web server.
You should need no additional files outside the `dist` folder.
## Development branch build
If you want to run the demo shell with the latest changes from the development branch, use the following command :
```sh
npm run start
```
+56
View File
@@ -0,0 +1,56 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html
process.env.CHROME_BIN = require('puppeteer').executablePath();
module.exports = function (config) {
config.set({
basePath: './',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma'),
require('karma-mocha-reporter')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
files: [],
preprocessors: {},
mime: {
'text/x-typescript': ['ts', 'tsx']
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, 'coverage'), reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
customLaunchers: {
ChromeHeadless: {
base: 'Chrome',
flags: [
'--no-sandbox',
'--headless',
'--disable-gpu',
'--remote-debugging-port=9222'
]
}
},
captureTimeout: 180000,
browserDisconnectTimeout: 180000,
browserDisconnectTolerance: 3,
browserNoActivityTimeout: 300000,
reporters: ['mocha', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['ChromeHeadless'],
singleRun: false
});
};
+21
View File
@@ -0,0 +1,21 @@
{
"name": "Alfresco-ADF-Angular-Demo",
"description": "Demo shell for Alfresco Angular components",
"version": "6.9.0",
"author": "Hyland Software, Inc. and its affiliates",
"repository": {
"type": "git",
"url": "https://github.com/Alfresco/alfresco-ng2-components.git"
},
"bugs": {
"url": "https://github.com/Alfresco/alfresco-ng2-components/issues"
},
"license": "Apache-2.0",
"keywords": [
"ng2",
"angular",
"angular2",
"alfresco"
],
"private": true
}
+201
View File
@@ -0,0 +1,201 @@
{
"name": "demoshell",
"$schema": "../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "demo-shell/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:browser",
"options": {
"allowedCommonJsDependencies": [
"minimatch",
"minimatch-browser",
"superagent",
"event-emitter",
"brace-expansion",
"zen-observable",
"subscriptions-transport-ws",
"d",
"chart.js",
"cropperjs"
],
"outputPath": "dist/demo-shell",
"index": "demo-shell/src/index.html",
"main": "demo-shell/src/main.ts",
"tsConfig": "tsconfig.dev.json",
"polyfills": "demo-shell/src/polyfills.ts",
"stylePreprocessorOptions": {
"includePaths": ["lib", "lib/core/src/lib"]
},
"assets": [
"demo-shell/src/assets",
"demo-shell/src/favicon-96x96.png",
"demo-shell/src/app.config.json",
{
"glob": "**/*",
"input": "demo-shell/src/assets",
"output": "/assets"
},
{
"glob": "app.config.json",
"input": "demo-shell/src",
"output": "/"
},
{
"glob": "**/*",
"input": "demo-shell/resources",
"output": "/resources"
},
{
"glob": "**/*",
"input": "lib/core/src/lib/assets",
"output": "/assets"
},
{
"glob": "**/*",
"input": "lib/process-services/src/lib/assets",
"output": "/assets"
},
{
"glob": "**/*",
"input": "lib/process-services-cloud/src/lib/assets",
"output": "/assets"
},
{
"glob": "**/*",
"input": "lib/content-services/src/lib/assets",
"output": "/assets"
},
{
"glob": "**/*",
"input": "lib/core/src/lib/i18n",
"output": "/assets/adf-core/i18n"
},
{
"glob": "**/*",
"input": "lib/content-services/src/lib/i18n",
"output": "/assets/adf-content-services/i18n"
},
{
"glob": "**/*",
"input": "lib/process-services/src/lib/i18n",
"output": "/assets/adf-process-services/i18n"
},
{
"glob": "**/*",
"input": "lib/process-services-cloud/src/lib/i18n",
"output": "/assets/adf-process-services-cloud/i18n"
},
{
"glob": "**/*",
"input": "lib/insights/src/lib/i18n",
"output": "/assets/adf-insights/i18n"
},
{
"glob": "pdf.worker.min.js",
"input": "node_modules/pdfjs-dist/build",
"output": "/"
},
{
"glob": "**/*",
"input": "node_modules/monaco-editor",
"output": "/assets/monaco/"
}
],
"styles": [
"demo-shell/src/styles.scss",
"demo-shell/src/custom-style-dev.scss",
"node_modules/cropperjs/dist/cropper.min.css",
"node_modules/pdfjs-dist/web/pdf_viewer.css"
],
"scripts": ["node_modules/pdfjs-dist/build/pdf.js", "node_modules/pdfjs-dist/web/pdf_viewer.js", "node_modules/raphael/raphael.min.js"],
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
},
"configurations": {
"production": {
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "12kb"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"buildOptimizer": true,
"verbose": false,
"fileReplacements": [
{
"replace": "demo-shell/src/environments/environment.ts",
"with": "demo-shell/src/environments/environment.prod.ts"
}
]
},
"canary": {
"fileReplacements": [
{
"replace": "demo-shell/src/environments/environment.ts",
"with": "demo-shell/src/environments/environment.canary.ts"
}
]
},
"e2e": {
"budgets": [
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"buildOptimizer": true,
"verbose": false,
"fileReplacements": [
{
"replace": "demo-shell/src/environments/environment.ts",
"with": "demo-shell/src/environments/environment.e2e.ts"
}
]
}
},
"defaultConfiguration": ""
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "demoshell:build",
"host": "0.0.0.0",
"port": 3000,
"proxyConfig": "demo-shell/proxy.conf.js",
"disableHostCheck": true
},
"configurations": {
"production": {
"browserTarget": "demoshell:build:production"
},
"canary": {
"browserTarget": "demoshell:build:canary"
},
"e2e": {
"browserTarget": "demoshell:build:e2e"
}
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["demo-shell/**/*.ts", "demo-shell/**/*.html"]
}
}
}
}
+76
View File
@@ -0,0 +1,76 @@
module.exports = {
getDeployedAppsProxy: function(processHost, deployedApps) {
let deployedAppProxy = {};
if (deployedApps) {
try {
const deployedAppsArray = JSON.parse(deployedApps);
for (const app of deployedAppsArray) {
const appName = app.name;
const appPath = `/${appName}`;
const appPathRewrite = `^/${appName}`;
deployedAppProxy = {
...deployedAppProxy,
[appPath]: {
target: `${processHost}`,
secure: false,
pathRewrite: {
[appPathRewrite]: appName,
},
changeOrigin: true,
},
};
}
} catch (e) {
console.log(e);
}
}
return deployedAppProxy;
},
getShareProxy: function(host) {
console.log('Target for /alfresco', host);
return {
'/alfresco': {
target: host,
secure: false,
logLevel: 'debug',
changeOrigin: true,
onProxyReq: function(request) {
if(request["method"] !== "GET")
request.setHeader("origin", host);
},
// workaround for REPO-2260
onProxyRes: function (proxyRes, req, res) {
const header = proxyRes.headers['www-authenticate'];
if (header && header.startsWith('Basic')) {
proxyRes.headers['www-authenticate'] = 'x' + header;
}
},
},
}
},
getApsProxy: function(host) {
console.log('Target for /activiti-app', host);
return {
'/activiti-app': {
target: host,
secure: false,
logLevel: 'debug',
changeOrigin: true,
},
}
},
getIdentityAdapterServiceProxy: function(host) {
console.log('Target for /identity-adapter-service', host);
return {
'/identity-adapter-service': {
target: host,
secure: false,
logLevel: 'debug',
changeOrigin: true,
},
}
}
};
+15
View File
@@ -0,0 +1,15 @@
require('dotenv').config();
const { getDeployedAppsProxy, getShareProxy, getApsProxy, getIdentityAdapterServiceProxy } = require('./proxy-helpers');
const legacyHost = process.env.PROXY_HOST_ADF;
const cloudHost = process.env.CLOUD_PROXY_HOST_ADF || process.env.PROXY_HOST_ADF;
const cloudApps = process.env.APP_CONFIG_APPS_DEPLOYED;
const apsHost = process.env.PROXY_HOST_ADF;
module.exports = {
...getShareProxy(legacyHost),
...getApsProxy(apsHost),
...getDeployedAppsProxy(cloudHost, cloudApps),
...getIdentityAdapterServiceProxy(cloudHost)
};
+39
View File
@@ -0,0 +1,39 @@
{
"title": "Welcome",
"NOTIFICATIONS": {
"TASK_ASSIGNED": "{{taskName}} task has been assigned to {{assignee}}",
"PROCESS_STARTED": "{{processName}} process has been started",
"TASK_UPDATED": "{{taskName}} task details have been updated",
"TASK_CREATED": "{{taskName}} task was created"
},
"SEARCH": {
"RESULTS": "Search results",
"NO_RESULT": "No results found",
"FACET_FIELDS": {
"TYPE": "1:Type",
"SIZE": "2:Size",
"CREATOR": "3:Creator",
"MODIFIER": "4:Modifier",
"CREATED": "5:Created"
},
"FACET_QUERIES": {
"MY_FACET_QUERIES": "My facet queries",
"CREATED_THIS_YEAR": "1.Created This Year",
"MIMETYPE": "2.Type: HTML",
"XTRASMALL": "3.Size: xtra small",
"SMALL": "4.Size: small",
"MEDIUM": "5.Size: medium",
"LARGE": "6.Size: large",
"XTRALARGE": "7.Size: xtra large",
"XXTRALARGE": "8.Size: XX large"
}
},
"GROUP-TITLE1-TRANSLATION-KEY": "CUSTOM TITLE TRANSLATION ONE",
"GROUP-TITLE2-TRANSLATION-KEY": "CUSTOM TITLE TRANSLATION TWO",
"ERROR_CONTENT": {
"507": {
"TITLE": "ACS Disk full",
"DESCRIPTION": "Content exceeds overall storage quota limit configured for the network or system"
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

File diff suppressed because it is too large Load Diff
+4
View File
@@ -0,0 +1,4 @@
<div class="app-demo-app-container">
<router-outlet></router-outlet>
<router-outlet name="overlay"></router-outlet>
</div>
+13
View File
@@ -0,0 +1,13 @@
router-outlet[name='overlay'] + * {
width: 100%;
height: 100%;
z-index: 999;
position: absolute;
top: 0;
right: 0;
}
.app-demo-app-container {
height: 100%;
width: 100%;
}
+62
View File
@@ -0,0 +1,62 @@
/*!
* @license
* Copyright © 2005-2024 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 { Component, ViewEncapsulation, OnInit } from '@angular/core';
import {
AuthenticationService,
PageTitleService
} from '@alfresco/adf-core';
import { Router } from '@angular/router';
import { MatDialog } from '@angular/material/dialog';
import { AdfHttpClient } from '@alfresco/adf-core/api';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class AppComponent implements OnInit {
constructor(private pageTitleService: PageTitleService,
private adfHttpClient: AdfHttpClient,
private authenticationService: AuthenticationService,
private router: Router,
private dialogRef: MatDialog) {
}
ngOnInit() {
this.pageTitleService.setTitle('title');
this.adfHttpClient.on('error', (error) => {
if (error.status === 401) {
if (!this.authenticationService.isLoggedIn()) {
this.dialogRef.closeAll();
this.router.navigate(['/login']);
}
}
if (error.status === 507) {
if (!this.authenticationService.isLoggedIn()) {
this.dialogRef.closeAll();
this.router.navigate(['error/507']);
}
}
});
}
}
+65
View File
@@ -0,0 +1,65 @@
/*!
* @license
* Copyright © 2005-2024 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 { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { NgChartsModule } from 'ng2-charts';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core';
import { AppConfigService, DebugAppConfigService, CoreModule, AuthModule, provideTranslations } from '@alfresco/adf-core';
import { ExtensionsModule } from '@alfresco/adf-extensions';
import { AppComponent } from './app.component';
import { appRoutes } from './app.routes';
import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
import { ContentModule } from '@alfresco/adf-content-services';
import { InsightsModule } from '@alfresco/adf-insights';
import { ProcessModule } from '@alfresco/adf-process-services';
import { environment } from '../environments/environment';
import { ProcessServicesCloudModule } from '@alfresco/adf-process-services-cloud';
import { RouterModule } from '@angular/router';
import { CoreAutomationService } from '../testing/automation.service';
@NgModule({
imports: [
BrowserModule,
environment.e2e ? NoopAnimationsModule : BrowserAnimationsModule,
RouterModule.forRoot(appRoutes, { useHash: true }),
AuthModule.forRoot({ useHash: true }),
HttpClientModule,
TranslateModule.forRoot(),
CoreModule.forRoot(),
ContentModule.forRoot(),
InsightsModule.forRoot(),
ProcessModule.forRoot(),
ProcessServicesCloudModule.forRoot(),
ExtensionsModule.forRoot(),
NgChartsModule,
MonacoEditorModule.forRoot()
],
declarations: [AppComponent],
providers: [
{ provide: AppConfigService, useClass: DebugAppConfigService }, // not use this service in production
provideTranslations('app', 'resources')
],
bootstrap: [AppComponent]
})
export class AppModule {
constructor(automationService: CoreAutomationService) {
automationService.setup();
}
}
+327
View File
@@ -0,0 +1,327 @@
/*!
* @license
* Copyright © 2005-2024 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 { Routes } from '@angular/router';
import { AuthGuard, AuthGuardEcm, ErrorContentComponent, AuthGuardBpm, AuthGuardSsoRoleService } from '@alfresco/adf-core';
import { AppLayoutComponent } from './components/app-layout/app-layout.component';
import { HomeComponent } from './components/home/home.component';
import { LogoutComponent } from './components/logout/logout.component';
import { ProcessServiceComponent } from './components/process-service/process-service.component';
import { ShowDiagramComponent } from './components/process-service/show-diagram.component';
import { FormViewerComponent } from './components/process-service/form-viewer.component';
import { FormNodeViewerComponent } from './components/process-service/form-node-viewer.component';
import { AppsViewComponent } from './components/process-service/apps-view.component';
import { SearchResultComponent } from './components/search/search-result.component';
import { FilesComponent } from './components/files/files.component';
import { AppFormComponent } from './components/form/app-form.component';
import { DemoPermissionComponent } from './components/permissions/demo-permissions.component';
import { AppComponent } from './app.component';
import { AppsCloudDemoComponent } from './components/cloud/apps-cloud-demo.component';
import { CloudLayoutComponent } from './components/cloud/cloud-layout.component';
import { TasksCloudDemoComponent } from './components/cloud/tasks-cloud-demo.component';
import { ProcessesCloudDemoComponent } from './components/cloud/processes-cloud-demo.component';
import { StartTaskCloudDemoComponent } from './components/cloud/start-task-cloud-demo.component';
import { StartProcessCloudDemoComponent } from './components/cloud/start-process-cloud-demo.component';
import { TaskDetailsCloudDemoComponent } from './components/cloud/task-details-cloud-demo.component';
import { CloudViewerComponent } from './components/cloud/cloud-viewer.component';
import { ProcessDetailsCloudDemoComponent } from './components/cloud/process-details-cloud-demo.component';
import { FormCloudDemoComponent } from './components/app-layout/cloud/form-demo/cloud-form-demo.component';
import { DemoErrorComponent } from './components/error/demo-error.component';
import { ProcessCloudLayoutComponent } from './components/cloud/process-cloud-layout.component';
import { AppSearchFilterChipsComponent } from './components/search/search-filter-chips.component';
import { FileViewComponent } from './components/file-view/file-view.component';
import { SettingsComponent } from './components/settings/settings.component';
import { AppLoginComponent } from './components/login/login.component';
import { TaskListDemoComponent } from './components/task-list-demo/task-list-demo.component';
import { ProcessListDemoComponent } from './components/process-list-demo/process-list-demo.component';
import { AppCardViewComponent } from './components/card-view/card-view.component';
export const appRoutes: Routes = [
{ path: 'login', component: AppLoginComponent },
{ path: 'logout', component: LogoutComponent },
{
path: 'settings',
component: SettingsComponent
},
{
path: 'files/:nodeId/view',
component: AppComponent,
canActivate: [AuthGuardEcm],
canActivateChild: [AuthGuardEcm],
outlet: 'overlay',
children: [
{
path: '',
component: FileViewComponent
}
]
},
{
path: 'files/:nodeId/:versionId/view',
component: AppComponent,
canActivate: [AuthGuardEcm],
canActivateChild: [AuthGuardEcm],
outlet: 'overlay',
children: [
{
path: '',
component: FileViewComponent
}
]
},
{
path: 'preview/blob',
component: AppComponent,
outlet: 'overlay',
pathMatch: 'full',
children: [
{
path: '',
component: FileViewComponent
}
]
},
{
path: '',
component: AppLayoutComponent,
canActivate: [AuthGuard],
children: [
{
path: '',
redirectTo: `/home`,
pathMatch: 'full'
},
{
path: 'card-view',
children: [
{
path: '',
component: AppCardViewComponent
}
]
},
{
path: '',
component: HomeComponent
},
{
path: 'home',
component: HomeComponent
},
{
path: 'cloud',
canActivate: [AuthGuardSsoRoleService],
data: { roles: ['ACTIVITI_ADMIN', 'ACTIVITI_USER'], redirectUrl: '/error/403' },
children: [
{
path: '',
data: { roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
component: AppsCloudDemoComponent
},
{
path: ':appName',
canActivate: [AuthGuardSsoRoleService],
data: { clientRoles: ['appName'], roles: ['ACTIVITI_USER'], redirectUrl: '/error/403' },
component: ProcessCloudLayoutComponent,
children: [
{
path: '',
component: CloudLayoutComponent,
children: [
{
path: 'tasks',
component: TasksCloudDemoComponent
},
{
path: 'processes',
component: ProcessesCloudDemoComponent
}
]
},
{
path: 'start-task',
component: StartTaskCloudDemoComponent
},
{
path: 'start-process',
component: StartProcessCloudDemoComponent
},
{
path: 'task-details/:taskId',
component: TaskDetailsCloudDemoComponent
},
{
path: 'task-details/:taskId/files/:nodeId/view',
component: CloudViewerComponent
},
{
path: 'process-details/:processInstanceId',
component: ProcessDetailsCloudDemoComponent
}
]
}
]
},
{
path: 'settings-layout',
component: SettingsComponent
},
{
path: 'files',
component: FilesComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'files/:id',
component: FilesComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'files/:id/display/:mode',
component: FilesComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'search',
component: SearchResultComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'search-filter-chips',
component: AppSearchFilterChipsComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'activiti',
component: AppsViewComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps',
component: AppsViewComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps/:appId/tasks',
component: ProcessServiceComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps/:appId/tasks/:filterId',
component: ProcessServiceComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps/:appId/processes',
component: ProcessServiceComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps/:appId/processes/:filterId',
component: ProcessServiceComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps/:appId/diagram/:processDefinitionId',
component: ShowDiagramComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'activiti/apps/:appId/report',
component: ProcessServiceComponent,
canActivate: [AuthGuardBpm]
},
// TODO: check if needed
{
path: 'activiti/appId/:appId',
component: ProcessServiceComponent,
canActivate: [AuthGuardBpm]
},
// TODO: check if needed
{
path: 'activiti/tasks/:id',
component: FormViewerComponent,
canActivate: [AuthGuardBpm]
},
// TODO: check if needed
{
/* cspell:disable-next-line */
path: 'activiti/tasksnode/:id',
component: FormNodeViewerComponent,
canActivate: [AuthGuardBpm]
},
{
path: 'permissions/:id',
component: DemoPermissionComponent,
canActivate: [AuthGuardEcm]
},
{ path: 'form-cloud', component: FormCloudDemoComponent },
{ path: 'form', component: AppFormComponent },
{
path: 'task-list',
canActivate: [AuthGuardBpm],
children: [
{
path: '',
component: TaskListDemoComponent
},
{
path: ':id',
component: TaskListDemoComponent
}
]
},
{
path: 'process-list',
canActivate: [AuthGuardBpm],
children: [
{
path: '',
component: ProcessListDemoComponent
},
{
path: ':id',
component: ProcessListDemoComponent
}
]
},
{
path: 'error/no-authorization',
component: ErrorContentComponent
}
]
},
{
path: 'error',
component: AppLayoutComponent,
children: [
{
path: '',
redirectTo: '/error/404',
pathMatch: 'full'
},
{
path: ':id',
component: DemoErrorComponent
}
]
},
{
path: '**',
redirectTo: 'error/404'
}
];
@@ -0,0 +1,93 @@
<adf-sidenav-layout
[sidenavMin]="70"
[sidenavMax]="220"
[stepOver]="780"
data-automation-id="sidenav-layout">
<adf-sidenav-layout-header>
<ng-template>
<adf-layout-header
id="adf-header"
title="ADF Demo Application"
redirectUrl="/home"
tooltip="ADF Demo Application"
[showSidenavToggle]="false">
<div class="app-layout-menu-spacer"></div>
<app-search-bar></app-search-bar>
<app-shell-user-info [menuPositionX]="'before'" [menuPositionY]="'above'"></app-shell-user-info>
<adf-avatar
size="32px"
src="https://avatars.githubusercontent.com/u/503991?v=4&size=64"
initials="DV"
tooltip="Denys Vuika"
cursor="pointer"
[matMenuTriggerFor]="userMenu">
</adf-avatar>
<mat-menu #userMenu="matMenu">
<button mat-menu-item>Item 1</button>
<button mat-menu-item>Item 2</button>
</mat-menu>
</adf-layout-header>
</ng-template>
</adf-sidenav-layout-header>
<adf-sidenav-layout-navigation>
<ng-template>
<mat-nav-list class="app-sidenav-linklist">
<ng-container *ngFor="let link of links">
<ng-container *ngIf="link.children">
<mat-list-item (click)="trigger.openMenu()" [attr.data-automation-id]="link.title | translate" class="app-sidenav-link">
<mat-icon matListItemIcon>{{link.icon}}</mat-icon>
<span matLine>{{ link.title | translate }}</span>
<mat-icon class="app-sidenav-link__expand-button" [matMenuTriggerData]="{links: link.children}"
rippleTrigger mat-icon-button #trigger="matMenuTrigger"
[matMenuTriggerFor]="nestedMenu">arrow_right</mat-icon>
</mat-list-item>
</ng-container>
<ng-container *ngIf="!link.children">
<mat-list-item [routerLink]="link.href"
routerLinkActive="app-sidenav-link--active" [routerLinkActiveOptions]="{ exact: true }"
[attr.data-automation-id]="link.title | translate" class="app-sidenav-link">
<mat-icon matListItemIcon >{{link.icon}}</mat-icon>
<span matLine>{{link.title | translate }}</span>
</mat-list-item>
</ng-container>
</ng-container>
<mat-list-item adf-logout [enableRedirect]="enableRedirect" redirectUri="/logout" class="app-sidenav-link" data-automation-id="Logout" >
<mat-icon matListItemIcon>exit_to_app</mat-icon>
<span matLine>Logout</span>
</mat-list-item>
</mat-nav-list>
</ng-template>
</adf-sidenav-layout-navigation>
<adf-sidenav-layout-content>
<ng-template>
<router-outlet></router-outlet>
</ng-template>
</adf-sidenav-layout-content>
</adf-sidenav-layout>
<mat-menu #nestedMenu="matMenu" xPosition="after" class="nestedMenu">
<ng-template matMenuContent let-links="links">
<button mat-menu-item *ngFor="let link of links"
class="app-sidenav-link"
[attr.data-automation-id]="link.title | translate"
routerLinkActive="app-sidenav-link--active"
[routerLink]="link.href"
[routerLinkActiveOptions]="{ exact: true }">
<mat-icon matListItemIcon>{{link.icon}}</mat-icon>
{{ link.title | translate }}
</button>
</ng-template>
</mat-menu>
<adf-file-uploading-dialog #fileDialog position="left"></adf-file-uploading-dialog>
@@ -0,0 +1,14 @@
.app-layout {
display: flex;
flex: 1;
min-width: 320px;
height: 100%;
.app-sidenav-link--active {
color: var(--theme-primary-color);
}
&-menu-spacer {
flex: 1 1 auto;
}
}
@@ -0,0 +1,104 @@
/*!
* @license
* Copyright © 2005-2024 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 { Component, ViewEncapsulation } from '@angular/core';
import {
AlfrescoApiService,
AvatarComponent,
HeaderLayoutComponent,
LogoutDirective,
SidenavLayoutComponent,
SidenavLayoutContentDirective,
SidenavLayoutHeaderDirective,
SidenavLayoutNavigationDirective
} from '@alfresco/adf-core';
import { SearchBarComponent } from '../search/search-bar.component';
import { UserInfoComponent } from './user-info/user-info.component';
import { MatMenuModule } from '@angular/material/menu';
import { MatListModule } from '@angular/material/list';
import { TranslateModule } from '@ngx-translate/core';
import { MatIconModule } from '@angular/material/icon';
import { MatLineModule } from '@angular/material/core';
import { CommonModule } from '@angular/common';
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
import { FileUploadingDialogComponent } from '@alfresco/adf-content-services';
@Component({
standalone: true,
imports: [
CommonModule,
SidenavLayoutComponent,
SidenavLayoutHeaderDirective,
HeaderLayoutComponent,
SearchBarComponent,
UserInfoComponent,
AvatarComponent,
MatMenuModule,
SidenavLayoutNavigationDirective,
MatListModule,
TranslateModule,
MatIconModule,
MatLineModule,
RouterLink,
RouterLinkActive,
LogoutDirective,
SidenavLayoutContentDirective,
RouterOutlet,
FileUploadingDialogComponent
],
templateUrl: './app-layout.component.html',
styleUrls: ['./app-layout.component.scss'],
host: { class: 'app-layout' },
encapsulation: ViewEncapsulation.None
})
export class AppLayoutComponent {
links: Array<any> = [
{ href: '/home', icon: 'home', title: 'Home' },
{ href: '/files', icon: 'folder_open', title: 'Content Services' },
{ href: '/card-view', icon: 'view_headline', title: 'CardView' },
{ href: '/task-list', icon: 'assignment', title: 'Task List' },
{
href: '/cloud',
icon: 'cloud',
title: 'Process Cloud',
children: [
{ href: '/cloud/', icon: 'cloud', title: 'Home' },
{ href: '/form-cloud', icon: 'poll', title: 'Form' }
]
},
{
href: '/activiti',
icon: 'device_hub',
title: 'Process Services',
children: [
{ href: '/activiti', icon: 'vpn_key', title: 'App' },
{ href: '/process-list', icon: 'assignment', title: 'Process List' },
{ href: '/form', icon: 'poll', title: 'Form' }
]
},
{ href: '/login', icon: 'vpn_key', title: 'Login' },
{ href: '/settings-layout', icon: 'settings', title: 'Settings' }
];
enableRedirect = true;
constructor(private alfrescoApiService: AlfrescoApiService) {
if (this.alfrescoApiService.getInstance().isOauthConfiguration()) {
this.enableRedirect = false;
}
}
}
@@ -0,0 +1,47 @@
<div class="main-content">
<mat-tab-group [animationDuration]="0">
<mat-tab label="Form" class="form-cloud-render">
<div class="app-form-container">
<adf-cloud-form
[showRefreshButton]="false"
[form]="form"
(formSaved)="onFormSaved()"
(formError)="logErrors($event)">
</adf-cloud-form>
</div>
<div class="app-console">
<h3>Error log:</h3>
<p *ngFor="let error of errorFields">Error {{ error.name }} {{error.validationSummary.message | translate}}</p>
</div>
</mat-tab>
<mat-tab label="Editor" class="form-cloud-editor">
<ngx-monaco-editor
id="adf-form-config-editor"
class="app-form-config-editor"
[options]="editorOptions"
[(ngModel)]="formConfig"
(onInit)="onInitFormEditor($event)">
</ngx-monaco-editor>
<div class="app-form-editor-buttons">
<button mat-raised-button id="app-form-config-save" (click)="onSaveFormConfig()">Save form config</button>
<button mat-raised-button id="app-form-config-clear" (click)="onClearFormConfig()">Clear form config</button>
<a mat-raised-button class="app-upload-config-button">
<mat-icon>file_upload</mat-icon>
<label for="upload-config-file">Upload JSON File</label>
<input
id="upload-config-file"
data-automation-id="upload-single-file"
type="file"
name="uploadConfig"
accept=".json"
(change)="onConfigAdded($event)">
</a>
</div>
</mat-tab>
</mat-tab-group>
</div>
@@ -0,0 +1,50 @@
@import 'styles/mat-selectors';
.app-form-container {
padding: 10px;
}
.app-main-content {
padding: 0 15px;
}
.app-console {
width: 60%;
display: inline-block;
vertical-align: top;
margin-left: 10px;
height: 500px;
overflow: scroll;
padding-bottom: 30px;
h3 {
margin-top: 0;
}
p {
display: block;
font-family: monospace;
margin: 0;
}
}
.app-form-config-editor {
height: 500px;
}
.app-form-editor-buttons {
display: flex;
flex-direction: row;
column-gap: 4px;
}
.app-upload-config-button {
input {
cursor: pointer;
right: 0;
opacity: 0;
position: absolute;
top: 0;
z-index: 4;
}
}
@@ -0,0 +1,129 @@
/*!
* @license
* Copyright © 2005-2024 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 { Component, OnDestroy, OnInit } from '@angular/core';
import { FormFieldModel, FormModel, FormRenderingService, NotificationService } from '@alfresco/adf-core';
import { CloudFormRenderingService, FormCloudModule, FormCloudService } from '@alfresco/adf-process-services-cloud';
import { Subscription } from 'rxjs';
import { CustomEditorComponent, CustomWidgetComponent } from '../../../cloud/custom-form-components/custom-editor.component';
import { CoreAutomationService } from '../../../../../testing/automation.service';
import { CommonModule } from '@angular/common';
import { MatTabsModule } from '@angular/material/tabs';
import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
import { TranslateModule } from '@ngx-translate/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
@Component({
templateUrl: './cloud-form-demo.component.html',
standalone: true,
imports: [CommonModule, MatTabsModule, FormCloudModule, MonacoEditorModule, TranslateModule, FormsModule, MatButtonModule, MatIconModule],
styleUrls: ['./cloud-form-demo.component.scss'],
providers: [{ provide: FormRenderingService, useClass: CloudFormRenderingService }]
})
export class FormCloudDemoComponent implements OnInit, OnDestroy {
form: FormModel;
errorFields: FormFieldModel[] = [];
formConfig: string;
editor: any;
private subscriptions: Subscription[] = [];
editorOptions = {
theme: 'vs-dark',
language: 'json',
autoIndent: true,
formatOnPaste: true,
formatOnType: true,
automaticLayout: true
};
constructor(
private notificationService: NotificationService,
private formService: FormCloudService,
private automationService: CoreAutomationService,
private formRenderingService: FormRenderingService
) {
this.formRenderingService.register({
'demo-widget': () => CustomEditorComponent,
'custom-editor': () => CustomEditorComponent,
'custom-string': () => CustomWidgetComponent,
'custom-datetime': () => CustomWidgetComponent,
'custom-file': () => CustomWidgetComponent,
'custom-number': () => CustomWidgetComponent,
'custom-something': () => CustomWidgetComponent,
'custom-boolean': () => CustomWidgetComponent,
'custom-date': () => CustomWidgetComponent,
custom: () => CustomWidgetComponent
});
}
logErrors(errorFields: FormFieldModel[]) {
this.errorFields = errorFields;
}
ngOnInit() {
this.formConfig = JSON.stringify(this.automationService.forms.getFormCloudDefinition());
this.parseForm();
}
onFormSaved() {
this.notificationService.openSnackMessage('Task has been saved successfully');
}
ngOnDestroy() {
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
this.subscriptions = [];
}
onInitFormEditor(editor) {
this.editor = editor;
setTimeout(() => {
this.editor.getAction('editor.action.formatDocument').run();
}, 1000);
}
parseForm() {
this.form = this.formService.parseForm(JSON.parse(this.formConfig));
}
onSaveFormConfig() {
try {
this.parseForm();
} catch (error) {
this.notificationService.openSnackMessage('Wrong form configuration');
}
}
onClearFormConfig() {
this.formConfig = '';
}
onConfigAdded($event: any): void {
const file = $event.currentTarget.files[0];
const fileReader = new FileReader();
fileReader.onload = () => {
this.formConfig = fileReader.result as string;
};
fileReader.readAsText(file);
this.onInitFormEditor(this.editor);
$event.target.value = '';
}
}
@@ -0,0 +1,101 @@
<div
id="userinfo_container"
[class.adf-userinfo-name-right]="showOnRight"
(keyup)="onKeyPress($event)"
tabindex="0"
role="button"
class="adf-userinfo-container"
*ngIf="canShow"
>
<span *ngIf="showName" id="adf-userinfo-ecm-name-display" class="adf-userinfo-name">
{{ecmUser | fullName}}
</span>
<button mat-button [matMenuTriggerFor]="menu" class="adf-userinfo-menu_button adf-content-userinfo-button"
data-automation-id="adf-user-profile">
<div class="adf-userinfo-button-profile" id="user-profile">
<div *ngIf="identityUser; else showEcmUserImage" id="identity-user-image">
<div *ngIf="ecmUser?.avatarId; else initialTemplate">
<div class="adf-userinfo-profile-container">
<img id="logged-user-img" [src]="getEcmAvatar(ecmUser.avatarId)" alt="user-info-profile-button"
class="adf-userinfo-profile-image"/>
</div>
</div>
<ng-template #initialTemplate>
<div [innerHTML]="identityUser | usernameInitials:'adf-userinfo-pic'"></div>
</ng-template>
</div>
<ng-template #showEcmUserImage>
<div id="ecm-user-image">
<div *ngIf="ecmUser.avatarId; else initialTemplate" class="adf-userinfo-profile-container">
<img id="logged-user-img" [src]="getEcmAvatar(ecmUser.avatarId)" alt="user-info-profile-button"
class="adf-userinfo-profile-image"/>
</div>
<ng-template #initialTemplate>
<div [outerHTML]="ecmUser | usernameInitials:'adf-userinfo-pic'"></div>
</ng-template>
</div>
</ng-template>
</div>
</button>
<mat-menu #menu="matMenu" id="user-profile-lists" [xPosition]="menuPositionX" [yPosition]="menuPositionY"
[overlapTrigger]="false" class="adf-userinfo-menu">
<mat-card appearance="outlined" *ngIf="mode === userInfoMode.CONTENT" class="adf-userinfo-card adf-content-userinfo-card">
<mat-card-header class="adf-userinfo-card-header" [style.background-image]="'url(' + ecmBackgroundImage + ')'">
<div *ngIf="ecmUser.avatarId; else initialTemplate"
class="adf-userinfo-profile-container adf-hide-small">
<img class="adf-userinfo-profile-picture" id="ecm-user-detail-image"
alt="ecm-profile-image" [src]="getEcmAvatar(ecmUser.avatarId)"/>
</div>
<ng-template #initialTemplate>
<div
[outerHTML]="ecmUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
</ng-template>
<div class="mat-headline-6" id="ecm-username">{{ecmUser | fullName}}</div>
</mat-card-header>
<mat-card-content>
<div class="adf-userinfo-supporting-text">
<div class="adf-userinfo-detail">
<h2 id="ecm-full-name"
class="adf-userinfo__detail-title">{{ecmUser | fullName}}</h2>
<span id="ecm-email"> {{ecmUser.email}} </span>
<a href="#/profile">{{ 'USER_PROFILE.LABELS.MY_PROFILE' | translate }}</a>
</div>
<div class="adf-userinfo-detail">
<span class="adf-userinfo__secondary-info" id="ecm-job-title-label">
{{ 'USER_PROFILE.LABELS.ECM.JOB_TITLE' | translate }}
<span id="ecm-job-title"> {{ ecmUser.jobTitle ? ecmUser.jobTitle : 'N/A' }} </span>
</span>
</div>
</div>
</mat-card-content>
</mat-card>
<mat-card appearance="outlined" *ngIf="mode === userInfoMode.CONTENT_SSO" class="adf-userinfo-card adf-content-userinfo-card">
<mat-card-header class="adf-userinfo-card-header"
[style.background-image]="'url(' + bpmBackgroundImage + ')'">
<div *ngIf="ecmUser.avatarId; else initialTemplate"
class="adf-userinfo-profile-container adf-hide-small">
<img class="adf-userinfo-profile-picture" id="ecm-user-detail-image"
alt="ecm-profile-image" [src]="getEcmAvatar(ecmUser.avatarId)"/>
</div>
<ng-template #initialTemplate>
<div
[outerHTML]="identityUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
</ng-template>
<div class="mat-headline-6" id="identity-username">{{identityUser | fullName}}</div>
</mat-card-header>
<mat-card-content>
<div class="adf-userinfo-supporting-text">
<div class="adf-userinfo-detail">
<h2 id="identity-full-name"
class="adf-userinfo__detail-title">{{identityUser | fullName}}</h2>
<span id="identity-email"> {{identityUser.email}} </span>
<a href="#/profile">
{{ 'USER_PROFILE.LABELS.MY_PROFILE' | translate }}</a>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-menu>
</div>
@@ -0,0 +1,154 @@
@import 'styles/flex';
.adf {
&-userinfo-container {
display: flex;
align-items: center;
padding: 0 5px;
}
&-userinfo-name-right {
flex-direction: row-reverse;
}
&-userinfo-name {
padding: 0 5px;
@include layout-bp(lt-md) {
display: none;
}
}
&-userinfo-pic {
background: var(--adf-theme-primary-300);
display: inline-block;
width: 40px;
height: 40px;
border-radius: 100px;
text-align: center;
font-weight: bolder;
font-size: var(--theme-adf-picture-1-font-size);
text-transform: uppercase;
vertical-align: middle;
line-height: 40px;
}
&-userinfo-profile-image {
background: var(--adf-theme-primary-300);
text-align: center;
border-radius: 90%;
width: 40px;
height: 40px;
margin-right: 0;
cursor: pointer;
vertical-align: middle;
margin-left: 0;
}
&-userinfo-profile-container {
display: inline-block;
}
&-userinfo-menu_button.adf-content-userinfo-button {
margin-right: 0;
border-radius: 90%;
padding: 0;
min-width: 40px;
height: 40px;
}
&-userinfo-card-header {
align-items: center;
display: flex;
justify-content: stretch;
line-height: normal;
height: 100px;
box-sizing: border-box;
}
&-userinfo-card.adf-content-userinfo-card {
padding: 0;
}
&-userinfo-supporting-text {
overflow: hidden;
padding: 32px;
column-count: 2;
display: flex;
justify-content: space-between;
@include layout-bp(lt-sm) {
padding: 10px;
}
}
&-userinfo__detail-title {
text-overflow: ellipsis;
}
&-userinfo__secondary-info {
font-size: var(--theme-body-1-font-size);
font-weight: 400;
letter-spacing: 0;
line-height: 18px;
align-items: flex-end;
}
&-userinfo-profile-picture {
background: var(--adf-theme-primary-300);
background-size: cover;
border-radius: 50%;
height: 80px;
width: 80px;
margin-left: 0;
margin-right: 8px;
}
&-userinfo-profile-initials {
text-transform: uppercase;
background-size: cover;
background-color: var(--adf-theme-primary-300);
border-radius: 50%;
height: 80px;
width: 80px;
margin-left: 0;
margin-right: 8px;
font-size: 35px;
font-weight: 400;
letter-spacing: 0;
line-height: 78px;
overflow: hidden;
display: flex;
justify-content: space-around;
}
&-userinfo-button-profile {
display: inline-block;
border: 0;
vertical-align: middle;
}
&-userinfo-detail {
text-align: left;
}
&-hide-small.adf-userinfo-profile-initials,
&-hide-small.adf-userinfo-profile-container {
@include layout-bp(lt-md) {
display: none;
}
}
}
@media only screen and (device-width >= 480px) {
.adf-content-userinfo-menu.adf-userinfo-menu {
max-height: 450px;
min-width: 450px;
overflow: auto;
padding: 0;
}
}
.adf-content-userinfo-menu.adf-userinfo-menu > div {
padding: 0;
}
@@ -0,0 +1,118 @@
/*!
* @license
* Copyright © 2005-2024 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 { FullNamePipe, IdentityUserModel, InitialUsernamePipe, UserInfoMode } from '@alfresco/adf-core';
import { Component, Input, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core';
import { MatMenuModule, MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu';
import { Subject } from 'rxjs';
import { EcmUserModel, PeopleContentService } from '@alfresco/adf-content-services';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { TranslateModule } from '@ngx-translate/core';
@Component({
selector: 'adf-content-user-info',
standalone: true,
imports: [CommonModule, FullNamePipe, MatMenuModule, MatButtonModule, InitialUsernamePipe, MatCardModule, TranslateModule],
templateUrl: './content-user-info.component.html',
styleUrls: ['./content-user-info.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ContentUserInfoComponent implements OnDestroy {
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
/** Determines if user is logged in. */
@Input()
isLoggedIn: boolean;
/** ECM user info. */
@Input()
ecmUser: EcmUserModel;
/** Identity user info. */
@Input()
identityUser: IdentityUserModel;
/** current mode. */
@Input()
mode: UserInfoMode = UserInfoMode.CONTENT;
/** Custom path for the background banner image for ACS users. */
@Input()
ecmBackgroundImage: string = './resources/images/ecm-background.png';
/** Custom path for the background banner image for APS users. */
@Input()
bpmBackgroundImage: string = './resources/images/bpm-background.png';
/** Custom choice for opening the menu at the bottom. Can be `before` or `after`. */
@Input()
menuPositionX: MenuPositionX = 'after';
/** Custom choice for opening the menu at the bottom. Can be `above` or `below`. */
@Input()
menuPositionY: MenuPositionY = 'below';
/** Shows/hides the username next to the user info button. */
@Input()
showName: boolean = true;
/**
* When the username is shown, this defines its position relative to the user info button.
* Can be `right` or `left`.
*/
@Input()
namePosition: string = 'right';
userInfoMode = UserInfoMode;
private destroy$ = new Subject();
constructor(private peopleContentService: PeopleContentService) { /* empty */ }
ngOnDestroy(): void {
this.destroy$.next(true);
this.destroy$.complete();
}
onKeyPress(event: KeyboardEvent) {
this.closeUserModal(event);
}
private closeUserModal($event: KeyboardEvent) {
if ($event.keyCode === 27) {
this.trigger.closeMenu();
}
}
stopClosing(event: Event) {
event.stopPropagation();
}
getEcmAvatar(avatarId: string): string {
return this.peopleContentService.getUserProfileImage(avatarId);
}
get showOnRight(): boolean {
return this.namePosition === 'right';
}
get canShow(): boolean {
return this.isLoggedIn && !!this.ecmUser && !!this.mode;
}
}
@@ -1,6 +1,6 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
* Copyright © 2005-2024 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.
@@ -0,0 +1,18 @@
/*!
* @license
* Copyright © 2005-2024 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 './content-user-info.component';
@@ -1,6 +1,6 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
* Copyright © 2005-2024 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.
@@ -0,0 +1,109 @@
<div
id="userinfo_container"
tabindex="0"
role="button"
[class.adf-userinfo-name-right]="showOnRight"
(keyup)="onKeyPress($event)"
class="adf-userinfo-container"
*ngIf="canShow"
>
<span *ngIf="showName" id="adf-userinfo-bpm-name-display" class="adf-userinfo-name">
{{bpmUser | fullName}}
</span>
<button mat-button [matMenuTriggerFor]="menu" class="adf-userinfo-menu_button"
data-automation-id="adf-user-profile">
<div class="adf-userinfo-button-profile" id="user-profile">
<div *ngIf="ecmUser; else showBpmUserImage" id="ecm-user-image">
<div *ngIf="ecmUser.avatarId; else initialTemplate" class="adf-userinfo-profile-container">
<img id="logged-user-img" [src]="getEcmAvatar(ecmUser.avatarId)" alt="user-info-profile-button"
class="adf-userinfo-profile-image"/>
</div>
<ng-template #initialTemplate>
<div [outerHTML]="ecmUser | usernameInitials:'adf-userinfo-pic'"></div>
</ng-template>
</div>
<ng-template #showBpmUserImage>
<div *ngIf="bpmUser" id="bpm-user-image">
<div *ngIf="bpmUser.pictureId; else initialTemplate" class="adf-userinfo-profile-container">
<img id="logged-user-img" [src]="getBpmUserImage()" alt="user-info-profile-button"
class="adf-userinfo-profile-image"/>
</div>
<ng-template #initialTemplate>
<div [outerHTML]="bpmUser | usernameInitials:'adf-userinfo-pic'"></div>
</ng-template>
</div>
</ng-template>
</div>
</button>
<mat-menu #menu="matMenu" id="user-profile-lists" [xPosition]="menuPositionX" [yPosition]="menuPositionY"
[overlapTrigger]="false" class="adf-userinfo-menu">
<mat-tab-group id="tab-group-env" (click)="stopClosing($event)" selectedIndex="0" role="menuitem"
class="adf-userinfo-tab" [class.adf-hide-tab]="!ecmUser">
<mat-tab label="{{ 'USER_PROFILE.TAB.CS' | translate }}" role="dialog"
*ngIf="mode===userInfoMode.ALL">
<mat-card appearance="outlined" class="adf-userinfo-card" *ngIf="ecmUser">
<mat-card-header class="adf-userinfo-card-header"
[style.background-image]="'url(' + ecmBackgroundImage + ')'">
<div *ngIf="ecmUser.avatarId; else initialTemplate"
class="adf-userinfo-profile-container adf-hide-small">
<img class="adf-userinfo-profile-picture" id="ecm-user-detail-image"
alt="ecm-profile-image" [src]="getEcmAvatar(ecmUser.avatarId)"/>
</div>
<ng-template #initialTemplate>
<div
[outerHTML]="ecmUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
</ng-template>
<div class="mat-headline-6" id="ecm-username">{{ecmUser | fullName}}</div>
</mat-card-header>
<mat-card-content>
<div class="adf-userinfo-supporting-text">
<div class="adf-userinfo-detail">
<h2 id="ecm-full-name"
class="adf-userinfo__detail-title">{{ecmUser | fullName}}</h2>
<span id="ecm-email"> {{ecmUser.email}} </span>
</div>
<div class="adf-userinfo-detail">
<span class="adf-userinfo__secondary-info" id="ecm-job-title-label">
{{ 'USER_PROFILE.LABELS.ECM.JOB_TITLE' | translate }}
<span id="ecm-job-title"> {{ ecmUser.jobTitle ? ecmUser.jobTitle : 'N/A' }} </span>
</span>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-tab>
<mat-tab id="bpm-panel" label="{{ 'USER_PROFILE.TAB.PS' | translate }}" role="dialog"
*ngIf="mode===userInfoMode.PROCESS || mode===userInfoMode.ALL">
<mat-card appearance="outlined" class="adf-userinfo-card">
<mat-card-header class="adf-userinfo-card-header"
[style.background-image]="'url(' + bpmBackgroundImage + ')'">
<img *ngIf="bpmUser.pictureId; else initialTemplate"
class="adf-userinfo-profile-picture adf-hide-small" id="bpm-user-detail-image"
alt="bpm-profile-image" [src]="getBpmUserImage()"/>
<ng-template #initialTemplate>
<div
[outerHTML]="bpmUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
</ng-template>
<div class="mat-headline-6" id="bpm-username">{{bpmUser | fullName}}</div>
</mat-card-header>
<mat-card-content>
<div class="adf-userinfo-supporting-text">
<div class="adf-userinfo-detail">
<h2 id="bpm-full-name"
class="adf-userinfo__detail-title">{{ bpmUser | fullName }}</h2>
<span id="bpm-email"> {{bpmUser.email}} </span>
</div>
<div class="adf-userinfo-detail">
<span id="bpm-tenant" class="adf-userinfo__secondary-info">
{{ 'USER_PROFILE.LABELS.BPM.TENANT' | translate }}
<span>{{ bpmUser.tenantName ? bpmUser.tenantName : '' }}</span>
</span>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-tab>
</mat-tab-group>
</mat-menu>
</div>
@@ -0,0 +1,174 @@
@import 'styles/flex';
@import 'styles/mat-selectors';
.adf {
&-userinfo-container {
display: flex;
align-items: center;
padding: 0 5px;
}
&-userinfo-name-right {
flex-direction: row-reverse;
}
&-userinfo-name {
padding: 0 5px;
@include layout-bp(lt-md) {
display: none;
}
}
&-userinfo-pic {
background: var(--adf-theme-primary-300);
display: inline-block;
width: 40px;
height: 40px;
border-radius: 100px;
text-align: center;
font-weight: bolder;
font-size: var(--theme-adf-picture-1-font-size);
text-transform: uppercase;
vertical-align: middle;
line-height: 40px;
}
&-userinfo-profile-image {
background: var(--adf-theme-primary-300);
text-align: center;
border-radius: 90%;
width: 40px;
height: 40px;
margin-right: 0;
cursor: pointer;
vertical-align: middle;
margin-left: 0;
}
&-userinfo-profile-container {
display: inline-block;
}
&-userinfo-menu_button#{$mat-button} {
margin-right: 0;
border-radius: 90%;
padding: 0;
min-width: 40px;
height: 40px;
}
&-userinfo-tab #{$mat-tab-header} {
align-self: center;
width: 100%;
min-width: 250px;
}
&-userinfo-tab #{$mat-tab-label-text} {
flex: auto;
font-weight: 500;
font-size: var(--theme-body-1-font-size);
text-transform: uppercase;
line-height: 48px;
text-align: center;
}
&-userinfo-card-header {
align-items: center;
display: flex;
justify-content: stretch;
line-height: normal;
height: 100px;
box-sizing: border-box;
}
&-userinfo-card#{$mat-card} {
padding: 0;
}
&-userinfo-supporting-text {
overflow: hidden;
padding: 32px;
column-count: 2;
display: flex;
justify-content: space-between;
@include layout-bp(lt-sm) {
padding: 10px;
}
}
&-userinfo__detail-title {
text-overflow: ellipsis;
}
&-userinfo__secondary-info {
font-size: var(--theme-body-1-font-size);
font-weight: 400;
letter-spacing: 0;
line-height: 18px;
align-items: flex-end;
}
&-userinfo-profile-picture {
background: var(--adf-theme-primary-300);
background-size: cover;
border-radius: 50%;
height: 80px;
width: 80px;
margin-left: 0;
margin-right: 8px;
}
&-userinfo-profile-initials {
text-transform: uppercase;
background-size: cover;
background-color: var(--adf-theme-primary-300);
border-radius: 50%;
height: 80px;
width: 80px;
margin-left: 0;
margin-right: 8px;
font-size: 35px;
font-weight: 400;
letter-spacing: 0;
line-height: 78px;
overflow: hidden;
display: flex;
justify-content: space-around;
}
&-userinfo-button-profile {
display: inline-block;
border: 0;
vertical-align: middle;
}
&-userinfo-detail {
text-align: left;
}
&-hide-tab.adf-userinfo-tab#tab-group-env {
display: none;
}
&-hide-small.adf-userinfo-profile-initials,
&-hide-small.adf-userinfo-profile-container {
@include layout-bp(lt-md) {
display: none;
}
}
}
@media only screen and (min-device-width: 480px) {
#{$mat-menu-panel}.adf-userinfo-menu {
max-height: 450px;
min-width: 450px;
overflow: auto;
padding: 0;
}
}
#{$mat-menu-panel}.adf-userinfo-menu #{$mat-menu-content} {
padding: 0;
}
@@ -0,0 +1,230 @@
/*!
* @license
* Copyright © 2005-2024 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 { CoreTestingModule, UserInfoMode } from '@alfresco/adf-core';
import { fakeEcmUser, fakeEcmUserNoImage } from '@alfresco/adf-content-services';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { ProcessUserInfoComponent } from './process-user-info.component';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatTabGroupHarness, MatTabHarness } from '@angular/material/tabs/testing';
const fakeBpmUser: any = {
apps: [],
capabilities: null,
company: 'fake-company',
created: 'fake-create-date',
email: 'fakeBpm@fake.com',
externalId: 'fake-external-id',
firstName: 'fake-bpm-first-name',
lastName: 'fake-bpm-last-name',
groups: [],
id: 'fake-id',
lastUpdate: 'fake-update-date',
latestSyncTimeStamp: 'fake-timestamp',
password: 'fake-password',
pictureId: 12,
status: 'fake-status',
tenantId: 'fake-tenant-id',
tenantName: 'fake-tenant-name',
tenantPictureId: 'fake-tenant-picture-id',
type: 'fake-type'
};
describe('ProcessUserInfoComponent', () => {
const profilePictureUrl = 'alfresco-logo.svg';
let component: ProcessUserInfoComponent;
let fixture: ComponentFixture<ProcessUserInfoComponent>;
let loader: HarnessLoader;
let element: HTMLElement;
const openUserInfo = () => {
fixture.detectChanges();
const imageButton = element.querySelector<HTMLButtonElement>('#logged-user-img');
imageButton.click();
fixture.detectChanges();
};
const whenFixtureReady = async () => {
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreTestingModule, ProcessUserInfoComponent]
});
fixture = TestBed.createComponent(ProcessUserInfoComponent);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
element = fixture.nativeElement;
spyOn(window, 'requestAnimationFrame').and.returnValue(1);
});
afterEach(() => {
fixture.destroy();
});
it('should not show any image if the user is not logged in', () => {
expect(element.querySelector('#userinfo_container')).toBeDefined();
expect(element.querySelector('#logged-user-img')).toBeNull();
});
it('should NOT have users immediately after ngOnInit', () => {
expect(element.querySelector('#userinfo_container')).toBeDefined();
expect(element.querySelector('#ecm_username')).toBeNull();
expect(element.querySelector('#bpm_username')).toBeNull();
expect(element.querySelector('#user-profile-lists')).toBeNull();
});
describe('when user is logged on bpm', () => {
beforeEach(async () => {
component.bpmUser = fakeBpmUser;
component.isLoggedIn = true;
});
it('should show full name next the user image', async () => {
await whenFixtureReady();
openUserInfo();
const bpmUserName = fixture.debugElement.query(By.css('#bpm-username'));
expect(element.querySelector('#userinfo_container')).not.toBeNull();
expect(bpmUserName).toBeDefined();
expect(bpmUserName).not.toBeNull();
expect(bpmUserName.nativeElement.innerHTML).toContain('fake-bpm-first-name fake-bpm-last-name');
});
it('should get the bpm current user image from the service', async () => {
spyOn(component, 'getBpmUserImage').and.returnValue(profilePictureUrl);
await whenFixtureReady();
expect(element.querySelector('#userinfo_container')).not.toBeNull();
expect(element.querySelector('#logged-user-img')).not.toBeNull();
expect(element.querySelector('#logged-user-img').getAttribute('src')).toContain(profilePictureUrl);
});
it('should show last name if first name is null', async () => {
component.bpmUser = {
firstName: null,
lastName: 'fake-last-name'
} as any;
await whenFixtureReady();
const fullNameElement = element.querySelector('#adf-userinfo-bpm-name-display');
fixture.detectChanges();
expect(element.querySelector('#userinfo_container')).toBeDefined();
expect(element.querySelector('#adf-userinfo-bpm-name-display')).not.toBeNull();
expect(fullNameElement.textContent).toContain('fake-last-name');
expect(fullNameElement.textContent).not.toContain('fake-first-name');
});
it('should not show the tabs', async () => {
await whenFixtureReady();
openUserInfo();
const tabGroupHost = await (await loader.getHarness(MatTabGroupHarness.with({ selector: '#tab-group-env' }))).host();
expect(await tabGroupHost.hasClass('adf-hide-tab')).toBeTruthy();
});
});
describe('when user is logged on bpm and ecm', () => {
beforeEach(async () => {
component.bpmUser = fakeBpmUser;
component.ecmUser = fakeEcmUser as any;
component.isLoggedIn = true;
component.mode = UserInfoMode.ALL;
});
it('should show the tabs', async () => {
await whenFixtureReady();
openUserInfo();
const tabGroupHost = await (await loader.getHarness(MatTabGroupHarness.with({ selector: '#tab-group-env' }))).host();
expect(await tabGroupHost.hasClass('adf-hide-tab')).toBeFalsy();
});
it('should get the bpm user information', async () => {
spyOn(component, 'getBpmUserImage').and.returnValue(profilePictureUrl);
await whenFixtureReady();
openUserInfo();
const bpmTab = await loader.getHarness(MatTabHarness.with({ label: 'USER_PROFILE.TAB.PS' }));
await bpmTab.select();
const bpmUsername = fixture.debugElement.query(By.css('#bpm-username'));
const bpmImage = fixture.debugElement.query(By.css('#bpm-user-detail-image'));
expect(element.querySelector('#userinfo_container')).not.toBeNull();
expect(bpmUsername).not.toBeNull();
expect(bpmImage).not.toBeNull();
expect(bpmImage.properties.src).toContain(profilePictureUrl);
expect(bpmUsername.nativeElement.textContent).toContain('fake-bpm-first-name fake-bpm-last-name');
expect(fixture.debugElement.query(By.css('#bpm-tenant')).nativeElement.textContent).toContain('fake-tenant-name');
});
it('should get the ecm user information', async () => {
spyOn(component, 'getEcmAvatar').and.returnValue(profilePictureUrl);
await whenFixtureReady();
openUserInfo();
const ecmUsername = fixture.debugElement.query(By.css('#ecm-username'));
const ecmImage = fixture.debugElement.query(By.css('#ecm-user-detail-image'));
fixture.detectChanges();
await fixture.whenStable();
expect(element.querySelector('#userinfo_container')).toBeDefined();
expect(ecmUsername).not.toBeNull();
expect(ecmImage).not.toBeNull();
expect(ecmImage.properties.src).toContain(profilePictureUrl);
expect(fixture.debugElement.query(By.css('#ecm-full-name')).nativeElement.textContent).toContain(
'fake-ecm-first-name fake-ecm-last-name'
);
expect(fixture.debugElement.query(By.css('#ecm-job-title')).nativeElement.textContent).toContain('job-ecm-test');
});
it('should show the ecm image if exists', async () => {
spyOn(component, 'getEcmAvatar').and.returnValue(profilePictureUrl);
await whenFixtureReady();
openUserInfo();
expect(element.querySelector('#userinfo_container')).toBeDefined();
expect(element.querySelector('#logged-user-img')).toBeDefined();
expect(element.querySelector('#logged-user-img').getAttribute('src')).toEqual(profilePictureUrl);
});
it('should show the ecm initials if the ecm user has no image', async () => {
component.ecmUser = fakeEcmUserNoImage as any;
await whenFixtureReady();
expect(element.querySelector('#userinfo_container')).toBeDefined();
expect(element.querySelector('[data-automation-id="user-initials-image"]').textContent).toContain('ff');
});
it('should show the tabs for the env', async () => {
await whenFixtureReady();
openUserInfo();
const tabGroup = await loader.getHarness(MatTabGroupHarness.with({ selector: '#tab-group-env' }));
const tabs = await tabGroup.getTabs();
expect(await (await tabGroup.host()).hasClass('adf-hide-tab')).toBeFalsy();
expect(tabs.length).toBe(2);
});
it('should not close the menu when a tab is clicked', async () => {
await whenFixtureReady();
openUserInfo();
const bpmTab = await loader.getHarness(MatTabHarness.with({ label: 'USER_PROFILE.TAB.PS' }));
bpmTab.select();
expect(fixture.debugElement.query(By.css('#user-profile-lists'))).not.toBeNull();
});
});
});
@@ -0,0 +1,125 @@
/*!
* @license
* Copyright © 2005-2024 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 { FullNamePipe, InitialUsernamePipe, UserInfoMode } from '@alfresco/adf-core';
import { EcmUserModel, PeopleContentService } from '@alfresco/adf-content-services';
import { Component, Input, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core';
import { MatMenuModule, MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu';
import { Subject } from 'rxjs';
import { PeopleProcessService } from '@alfresco/adf-process-services';
import { UserRepresentation } from '@alfresco/js-api';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatTabsModule } from '@angular/material/tabs';
import { TranslateModule } from '@ngx-translate/core';
import { MatCardModule } from '@angular/material/card';
@Component({
selector: 'adf-process-user-info',
standalone: true,
imports: [CommonModule, FullNamePipe, MatButtonModule, MatMenuModule, InitialUsernamePipe, MatTabsModule, TranslateModule, MatCardModule],
templateUrl: './process-user-info.component.html',
styleUrls: ['./process-user-info.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ProcessUserInfoComponent implements OnDestroy {
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
/** Determines if user is logged in. */
@Input()
isLoggedIn: boolean;
/** BPM user info. */
@Input()
bpmUser: UserRepresentation;
/** ECM user info. */
@Input()
ecmUser: EcmUserModel;
/** current mode. */
@Input()
mode: UserInfoMode = UserInfoMode.PROCESS;
/** Custom path for the background banner image for APS users. */
@Input()
bpmBackgroundImage: string = './resources/images/bpm-background.png';
/** Custom path for the background banner image for ACS users. */
@Input()
ecmBackgroundImage: string = './resources/images/ecm-background.png';
/** Custom choice for opening the menu at the bottom. Can be `before` or `after`. */
@Input()
menuPositionX: MenuPositionX = 'after';
/** Custom choice for opening the menu at the bottom. Can be `above` or `below`. */
@Input()
menuPositionY: MenuPositionY = 'below';
/** Shows/hides the username next to the user info button. */
@Input()
showName: boolean = true;
/**
* When the username is shown, this defines its position relative to the user info button.
* Can be `right` or `left`.
*/
@Input()
namePosition: string = 'right';
userInfoMode = UserInfoMode;
private destroy$ = new Subject();
constructor(private peopleProcessService: PeopleProcessService, private peopleContentService: PeopleContentService) { /* empty */ }
ngOnDestroy(): void {
this.destroy$.next(true);
this.destroy$.complete();
}
onKeyPress(event: KeyboardEvent) {
this.closeUserModal(event);
}
private closeUserModal($event: KeyboardEvent) {
if ($event.keyCode === 27) {
this.trigger.closeMenu();
}
}
stopClosing(event: Event) {
event.stopPropagation();
}
getBpmUserImage(): string {
return this.peopleProcessService.getCurrentUserProfileImage();
}
getEcmAvatar(avatarId: string): string {
return this.peopleContentService.getUserProfileImage(avatarId);
}
get showOnRight(): boolean {
return this.namePosition === 'right';
}
get canShow(): boolean {
return this.isLoggedIn && !!this.bpmUser && !!this.mode;
}
}
@@ -0,0 +1,18 @@
/*!
* @license
* Copyright © 2005-2024 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 './process-user-info.component';
@@ -0,0 +1,21 @@
<ng-container>
<adf-content-user-info
*ngIf="mode === userInfoMode.CONTENT || mode === userInfoMode.CONTENT_SSO"
[ecmUser]="ecmUser$ | async"
[identityUser]="identityUser$ | async"
[isLoggedIn]="isLoggedIn"
[mode]="mode"
></adf-content-user-info>
<adf-identity-user-info
*ngIf="mode === userInfoMode.SSO"
[identityUser]="identityUser$ | async"
[isLoggedIn]="isLoggedIn"
></adf-identity-user-info>
<adf-process-user-info
*ngIf="mode === userInfoMode.PROCESS || mode === userInfoMode.ALL"
[bpmUser]="bpmUser$ | async"
[ecmUser]="ecmUser$ | async"
[isLoggedIn]="isLoggedIn"
[mode]="mode"
></adf-process-user-info>
</ng-container>
@@ -0,0 +1,124 @@
/*!
* @license
* Copyright © 2005-2024 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 { EcmUserModel, PeopleContentService } from '@alfresco/adf-content-services';
import { PeopleProcessService } from '@alfresco/adf-process-services';
import {
AuthenticationService,
BasicAlfrescoAuthService,
IdentityUserInfoComponent,
IdentityUserModel,
IdentityUserService,
UserInfoMode
} from '@alfresco/adf-core';
import { Component, OnInit, Input } from '@angular/core';
import { MenuPositionX, MenuPositionY } from '@angular/material/menu';
import { Observable, of } from 'rxjs';
import { CommonModule } from '@angular/common';
import { UserRepresentation } from '@alfresco/js-api';
import { ContentUserInfoComponent } from './content-user-info';
import { ProcessUserInfoComponent } from './process-user-info';
@Component({
selector: 'app-shell-user-info',
standalone: true,
imports: [CommonModule, ContentUserInfoComponent, IdentityUserInfoComponent, ProcessUserInfoComponent],
templateUrl: './user-info.component.html'
})
export class UserInfoComponent implements OnInit {
/** Custom choice for opening the menu at the bottom. Can be `before` or `after`. */
@Input()
menuPositionX: MenuPositionX = 'after';
/** Custom choice for opening the menu at the bottom. Can be `above` or `below`. */
@Input()
menuPositionY: MenuPositionY = 'below';
mode: UserInfoMode;
ecmUser$: Observable<EcmUserModel>;
bpmUser$: Observable<UserRepresentation>;
identityUser$: Observable<IdentityUserModel>;
userInfoMode = UserInfoMode;
constructor(
private peopleContentService: PeopleContentService,
private peopleProcessService: PeopleProcessService,
private identityUserService: IdentityUserService,
private basicAlfrescoAuthService: BasicAlfrescoAuthService,
private authService: AuthenticationService
) { /* empty */ }
ngOnInit() {
this.getUserInfo();
}
getUserInfo() {
if (this.authService.isOauth()) {
this.loadIdentityUserInfo();
this.mode = UserInfoMode.SSO;
if (this.authService.isECMProvider() && this.authService.isEcmLoggedIn()) {
this.mode = UserInfoMode.CONTENT_SSO;
this.loadEcmUserInfo();
}
} else if (this.isAllLoggedIn()) {
this.loadEcmUserInfo();
this.loadBpmUserInfo();
this.mode = UserInfoMode.ALL;
} else if (this.isEcmLoggedIn()) {
this.loadEcmUserInfo();
this.mode = UserInfoMode.CONTENT;
} else if (this.isBpmLoggedIn()) {
this.loadBpmUserInfo();
this.mode = UserInfoMode.PROCESS;
}
}
get isLoggedIn(): boolean {
if (this.basicAlfrescoAuthService.isKerberosEnabled()) {
return true;
}
return this.authService.isLoggedIn();
}
private loadEcmUserInfo(): void {
this.ecmUser$ = this.peopleContentService.getCurrentUserInfo();
}
private loadBpmUserInfo() {
this.bpmUser$ = this.peopleProcessService.getCurrentUserInfo();
}
private loadIdentityUserInfo() {
this.identityUser$ = of(this.identityUserService.getCurrentUserInfo());
}
private isAllLoggedIn() {
return (
(this.authService.isEcmLoggedIn() && this.authService.isBpmLoggedIn()) ||
(this.authService.isALLProvider() && this.basicAlfrescoAuthService.isKerberosEnabled())
);
}
private isBpmLoggedIn() {
return this.authService.isBpmLoggedIn() || (this.authService.isECMProvider() && this.basicAlfrescoAuthService.isKerberosEnabled());
}
private isEcmLoggedIn() {
return this.authService.isEcmLoggedIn() || (this.authService.isECMProvider() && this.basicAlfrescoAuthService.isKerberosEnabled());
}
}

Some files were not shown because too many files have changed in this diff Show More