Add missing documentation for extension loader callbacks (#1457)

* Add missing documentation for extension loader callbacks

* Fix messed up pipeline, how it could work until now???

* Travis yml fix

* Travis yml fix
This commit is contained in:
Popovics András 2020-05-07 23:23:11 +02:00 committed by GitHub
parent 89e7446bd2
commit 0e7eed3cd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 145 additions and 21 deletions

View File

@ -26,7 +26,7 @@ before_install:
- sleep 3 - sleep 3
- . ./scripts/ci/job_hooks/before_install.sh - . ./scripts/ci/job_hooks/before_install.sh
install: ./scripts/install.sh install: echo "no install"
stages: stages:
- name: Quality and Unit tests - name: Quality and Unit tests
@ -44,17 +44,26 @@ jobs:
include: include:
- stage: Quality and Unit tests - stage: Quality and Unit tests
name: 'Code quality checks' name: 'Code quality checks'
script: npm run lint script: npm ci && npm run lint
- name: 'Build no animation'
script: npm run build.e2e - stage: Quality and Unit tests
name: 'Build (without animation)'
script: npm ci && npm run build.e2e
after_success: ./scripts/ci/utils/artifact-to-s3.sh -a ./dist/app -o "$S3_DBP_FOLDER/alfresco-content-app.tar.bz2" after_success: ./scripts/ci/utils/artifact-to-s3.sh -a ./dist/app -o "$S3_DBP_FOLDER/alfresco-content-app.tar.bz2"
- name: 'Unit tests aos' cache: false
script:
- ng test adf-office-services-ext --watch=false - stage: Quality and Unit tests
- name: 'Unit tests ACA' name: 'Unit tests aos'
script: npm ci && ng test adf-office-services-ext --watch=false
cache: false
- stage: Quality and Unit tests
name: 'Unit tests ACA'
script: script:
- npm ci
- ng test app --code-coverage --watch=false - ng test app --code-coverage --watch=false
- bash <(curl -s https://codecov.io/bash) -X gcov - bash <(curl -s https://codecov.io/bash) -X gcov
cache: false
- stage: e2e - stage: e2e
name: Test Suite appNavigation&search name: Test Suite appNavigation&search

View File

@ -3,7 +3,7 @@
"javascript.preferences.importModuleSpecifier": "relative", "javascript.preferences.importModuleSpecifier": "relative",
"typescript.preferences.quoteStyle": "single", "typescript.preferences.quoteStyle": "single",
"typescript.preferences.importModuleSpecifier": "relative", "typescript.preferences.importModuleSpecifier": "relative",
"editor.formatOnSave": true, "editor.formatOnSave": false,
"[json]": { "[json]": {
"editor.formatOnSave": false "editor.formatOnSave": false
}, },

View File

@ -21,5 +21,6 @@ Learn how to extend the features of the Alfresco Content Application.
- [Custom icons](/extending/icons) - [Custom icons](/extending/icons)
- [Registration](/extending/registration) - [Registration](/extending/registration)
- [Creating custom evaluators](/extending/creating-custom-evaluators) - [Creating custom evaluators](/extending/creating-custom-evaluators)
- [Custom extension loaders](/extending/custom-extension-loaders.md)
- [Tutorials](/extending/tutorials) - [Tutorials](/extending/tutorials)
- [Redistributable libraries](/extending/redistributable-libraries) - [Redistributable libraries](/extending/redistributable-libraries)

View File

@ -0,0 +1,112 @@
---
Title: Custom extension loaders
---
# Creating an extension loader
There can be scenarios when there is a need to be able to preload some data, doing some health check or just kick off some services to be sure that a particular extension/plugin is set up properly and ready to function.
## Lifecycle
The extension loader callbacks are invoked when hitting the `root logged-in route` (''), which means they are called (once per application lifetime), when a user wants to access any of the authentication protected routes.
```ts
export const APP_ROUTES: Routes = [
{
path: 'login',
component: LoginComponent,
data: {
title: 'APP.SIGN_IN'
}
},
...// more unauthenticated routes
{
path: '',
component: AppLayoutComponent,
canActivate: [AuthGuardEcm, ExtensionsDataLoaderGuard],
children: [
...// more authenticated routes
{
path: 'trashcan',
loadChildren: './components/trashcan/trashcan.module#AppTrashcanModule'
}
],
canActivateChild: [AuthGuardEcm]
}
];
```
The Alfresco Content App provided `ExtensionsDataLoaderGuard` router guards executes the registered callback in the order they were registered.
## Examples
- Ensuring that a backend service is running and showing the extension provided features only if the service is working correctly.
- Preloading some data after the application is started but before it got rendered in an asynchronous way, which might be needed for an extension to work.
## Registering a loader for an extension
For this each extension can register its own loader, which can be either synchronous or asynchronous. The application is going to be rendered only after every extension loader is either finished or any of them errored.
This registering can be done, using Angular's multi-provider capabilities:
```ts
import { EXTENSION_DATA_LOADERS } from '@alfresco/aca-shared';
@NgModule({
imports: [...],
declarations: [...],
entryComponents: [...],
providers: [
...
{
provide: EXTENSION_DATA_LOADERS,
multi: true,
useValue: myExtensionLoader
},
...
],
})
export class MyExtensionModule {
```
1. `MyExtensionModule` is the extension's entry module, which needs to be imported by the extensions.module.ts.
2. `EXTENSION_DATA_LOADERS` is an injection token which can be imported from the `aca-shared` package.
3. `myExtensionLoader` is the function which preloads data / sets the necessary services for the extension.
### ExtensionLoaderCallback
The signature of an extension loader callback is the following:
```ts
type ExtensionLoaderCallback = (route: ActivatedRouteSnapshot) => Observable<boolean>;
```
The callback needs to return an Observable of boolean (of success). The boolean value returned, from application loading/rendering perspective doesn't really matter, it is rather representative, it doesn't block the application to be rendered.
Because of the following behaviour mentioned above, each extension's loader might implement its own error handling within itself, to provide a better User eXperience by for example notifying the user with an error snackbar about the problem.
```ts
export const myExtensionLoader = (route: ActivatedRouteSnapshot) => {
if (!isExtensionEnabled()) {
// Do nothing, simply let the next extension's loader callback invoked, if any
// The returned boolean doesn't matter here, doesn't block anything
return of(true);
}
// The returned boolean doesn't matter here, doesn't block anything
return myExtensionService.checkBackendHealth().pipe(
tap((status) => {
if (!status) {
// If the BE is down, let the user know what to expect
store.dispatch(
new SnackbarErrorAction("Backend error. My Extension's features are disabled.")
);
}
})
);
};
```
## Warning
Always handle (catch) the errors in your extension loader! Although an uncaught, errored extension loader can't prevent the application to be rendered, but it can short circuit any of the other extension loaders. (This is coming from the nature of how the forkJoin is implemented in the RxJs library.)
This would essentially mean, that if you have 3 extensions, with registered asynchronous loaders for each, in case of one of them errors, the application doesn't wait the other 2 to be finished, but continues to render. Which can result that the other (2) not errored extension won't behave properly, since the application rendering haven't waited until their loaders were finished.

View File

@ -30,7 +30,7 @@ import { map, catchError } from 'rxjs/operators';
export type ExtensionLoaderCallback = ( export type ExtensionLoaderCallback = (
route: ActivatedRouteSnapshot route: ActivatedRouteSnapshot
) => Observable<true>; ) => Observable<boolean>;
export function DefaultExtensionLoaderFactory() { export function DefaultExtensionLoaderFactory() {
return []; return [];

View File

@ -1,5 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Run the build on mergin to development with always the latest ADF
if [ "${TRAVIS_BRANCH}" == "develop" ] && [ "${TRAVIS_EVENT_TYPE}" == "push" ]; then
./scripts/update-version.sh -v --$ADF_RELEASE_VERSION
fi
pip install --user awscli pip install --user awscli
export NODE_OPTIONS="--max_old_space_size=30000" export NODE_OPTIONS="--max_old_space_size=30000"

View File

@ -1,7 +0,0 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [ "${TRAVIS_BRANCH}" != "master" ]; then
./scripts/update-version.sh -v alpha
fi

View File

@ -23,12 +23,16 @@ set_version() {
} }
update(){ update(){
for (( j=0; j<${libslength}; j++ )); eval libsWithVersions=();
for (( i=0; i<${libslength}; i++ ));
do do
EXACT_VERSION="${libs[$j]}@${VERSION}" EXACT_VERSION="${libs[$i]}@${VERSION}"
echo "====== ${EXACT_VERSION} ======" eval libsWithVersions=( "${libsWithVersions[@]}" "${EXACT_VERSION}" )
npm i -E ${EXACT_VERSION}
done done
echo "npm i --ignore-scripts -E ${libsWithVersions[*]}"
npm i --ignore-scripts -E ${libsWithVersions[*]}
} }
while [[ $1 == -* ]]; do while [[ $1 == -* ]]; do