libs/ngx-pfe/pfe-actions/pfe-actions.service.ts
Properties |
|
Methods |
|
constructor(pfeStateService: PfeStateService, pfeUpdateStateValuesService: PfeUpdateStateValuesService, pfeUpdateStateOnBackendService: PfeUpdateStateOnBackendService, pfeConfigurationService: PfeConfigurationService, pfeRewindHistoryService: PfeRewindHistoryService, pfeResetStateService: PfeResetStateService, pfeConditionsService: PfeConditionsService, logger: NgxLoggerService)
|
|||||||||||||||||||||||||||
Parameters :
|
Public Async executeAction | ||||||||||||
executeAction(actionCfg: PfeBaseActionConfig, interruptBusyState?: (value?: boolean) => void)
|
||||||||||||
Get the global actions. Then spread the action with the global action (if needed) Then execute the action
Parameters :
Returns :
Promise<boolean>
|
Public Async executeActions | ||||||||||||
executeActions(actions: PfeBaseActionConfig[], interruptBusyState?: (value?: boolean) => void)
|
||||||||||||
Execute an array of actions
Parameters :
Returns :
Promise<boolean>
The combined boolean response of the actions. True if all returned true. False if at least one returned false. |
Public registerAction | ||||||||||||||||
registerAction(type: string, action: PfeActionFunction, options?: RegisterActionOptions)
|
||||||||||||||||
Register an action that can be invoked later to see examples
Parameters :
Returns :
void
|
Public actionExecuted$ |
Default value : this._actionExecuted$.asObservable()
|
Public registeredActions$ |
Default value : new ReplaySubject<string[]>(1)
|
import { NgxLoggerService } from '@allianz/ngx-logger';
import { Injectable } from '@angular/core';
import { ReplaySubject, Subject } from 'rxjs';
import { PfeConditionsService } from '../pfe-conditions/public-api';
import { PfeConfigurationService } from '../services/pfe-config-service/config-service.service';
import { PfeStateService } from './../services/pfe-state-service/state.service';
import { PfeNestedActions, PfeNestedActionsType } from './nested-actions/nested-actions.model';
import {
ExecutionResult,
GlobalActions,
PfeActionConfig,
PfeActionFunction,
PfeBaseActionConfig,
PfeGlobalAction,
} from './pfe-actions.model';
import { reloadPageAction } from './reload-page/reload-page-action';
import { ReloadPageActionType } from './reload-page/reload-page-action.model';
import { PfeResetStateActionType } from './reset-state/reset-state.model';
import { PfeResetStateService } from './reset-state/reset-state.service';
import { PfeRewindHistory } from './rewind-history/rewind-history.model';
import { PfeRewindHistoryService } from './rewind-history/rewind-history.service';
import { PfeUpdateStateOnBackend } from './update-state-on-backend/update-state-on-backend.model';
import { PfeUpdateStateOnBackendService } from './update-state-on-backend/update-state-on-backend.service';
import { PfeUpdateStateValuesType } from './update-state-values/update-state-values.model';
import { PfeUpdateStateValuesService } from './update-state-values/update-state-values.service';
interface RegisteredAction {
func: PfeActionFunction;
options?: RegisterActionOptions;
}
interface RegisterActionOptions {
/**
* Use `notBusyWhileRunning: true` to let PFE know that the app should not be marked as busy while this action is running.
* This means, `busy$` in the PfeBusinessService is going to be `false` for as long as this action is active.
* Currently this is only supported for `onPageLeaveActions` and `onNavigationStartActions`.
*/
notBusyWhileRunning?: boolean;
}
@Injectable()
export class PfeActionsService {
private registeredActions = new Map<string, RegisteredAction>();
public registeredActions$ = new ReplaySubject<string[]>(1);
private _actionExecuted$ = new Subject<PfeBaseActionConfig>();
// eslint-disable-next-line @typescript-eslint/member-ordering
public actionExecuted$ = this._actionExecuted$.asObservable();
constructor(
protected pfeStateService: PfeStateService,
protected pfeUpdateStateValuesService: PfeUpdateStateValuesService,
protected pfeUpdateStateOnBackendService: PfeUpdateStateOnBackendService,
protected pfeConfigurationService: PfeConfigurationService,
protected pfeRewindHistoryService: PfeRewindHistoryService,
protected pfeResetStateService: PfeResetStateService,
protected pfeConditionsService: PfeConditionsService,
protected logger: NgxLoggerService
) {
this.registerDefaultActions();
}
/**
* Execute an array of actions
* @param actions The array of action
* @param interruptBusyState Optional callback to interrupt PFE's busy state if an action is marked as `notBusyWhileRunning`
* @returns The combined boolean response of the actions. True if all returned true. False if at least one returned false.
*/
public async executeActions(actions: PfeBaseActionConfig[], interruptBusyState?: (value: boolean) => void): Promise<boolean> {
if (!actions || (actions && !actions.length)) {
return true;
}
const globalActions = await this.getGlobalActions();
for (let action of actions) {
try {
action = await this.getGlobalAction(action, globalActions);
const actionResult = await this.executeSpreadAction(action as PfeGlobalAction, interruptBusyState);
if (!actionResult.actionResult || (actionResult.wasExecuted && action.break)) {
return actionResult.actionResult;
}
} catch (error) {
this.logger.errorToServer(`Error on ACTION "${action.type || action.globalConfigId}"`, (error as Error).message);
throw error;
}
}
return true;
}
/**
* Get the global actions. Then spread the action with the global action (if needed)
* Then execute the action
* @param actionCfg Action configuration
* @param interruptBusyState Function to call to interrupt PFE's busy state if an action is waiting for user input
*/
public async executeAction(actionCfg: PfeBaseActionConfig, interruptBusyState?: (value: boolean) => void): Promise<boolean> {
const action = await this.getGlobalAction(actionCfg);
return (await this.executeSpreadAction(action, interruptBusyState)).actionResult;
}
/**
* Register an action that can be invoked later
* @param type The name of the action
* @param action The action itself, should be an arrow async function, see the default actions
* to see examples
* @param options Further options for the action.
*/
public registerAction(type: string, action: PfeActionFunction, options?: RegisterActionOptions) {
if (this.registeredActions.has(type)) {
throw new RangeError(`The action "${type}" its already registered`);
}
this.registeredActions.set(type, { func: action, options });
this.registeredActions$.next(Array.from(this.registeredActions.keys()));
}
/**
* Returns the action if there is no globalAction.
* Else will return the action on the global action spread with the content of the action
* @param actionCfg The action cfg
* @param globalActionsCfg The global action config
*/
protected async getGlobalAction(
actionCfg: PfeBaseActionConfig,
globalActionsCfg?: GlobalActions<PfeGlobalAction> | undefined
): Promise<PfeGlobalAction> {
if (!actionCfg.globalConfigId) {
return actionCfg as PfeGlobalAction;
}
if (!globalActionsCfg) {
globalActionsCfg = await this.getGlobalActions();
}
if (!globalActionsCfg) {
return actionCfg as PfeGlobalAction;
}
const id = actionCfg.globalConfigId;
const globalAction = globalActionsCfg[id];
if (!globalAction) {
throw new RangeError(`The action with id "${id}" is not declared on the global actions`);
}
return {
...globalAction,
...actionCfg,
} as PfeGlobalAction;
}
/**
* Execute a spread action
* @param action The action to execute
* @returns The result that was returned from the action
*/
private async executeSpreadAction(action: PfeGlobalAction, interruptBusyState?: (value: boolean) => void): Promise<ExecutionResult> {
this.checkIfActionIsRegistered(action);
const shouldExecuteAction = action.conditions
? this.pfeConditionsService.evaluateConditions(action.conditions, this.pfeStateService.getFullState())
: true;
if (shouldExecuteAction) {
// The get can never be undefined as checkIfActionIsRegistered() already ensures that.
// See also: https://github.com/microsoft/TypeScript/issues/9619
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const { func, options } = this.registeredActions.get(action.type)!;
if (options?.notBusyWhileRunning) {
interruptBusyState?.(true);
}
const result = await func(action).finally(() => {
if (options?.notBusyWhileRunning) {
interruptBusyState?.(false);
}
});
this._actionExecuted$.next(action);
// If the action returned something not boolean, we default to true:
return {
wasExecuted: true,
actionResult: typeof result === 'boolean' ? result : true,
};
} else {
return {
wasExecuted: false,
actionResult: true,
};
}
}
/**
* Check if an action its registered and if no it throws an error
* @param action An action
* @throws RangeError if the action its not registered
*/
private checkIfActionIsRegistered(action: PfeBaseActionConfig) {
if (!action?.type) {
throw new Error('The given action config does not have a type');
}
if (!this.registeredActions.has(action.type)) {
throw new RangeError(`The action "${action.type}" is not registered.`);
}
}
/**
* Get the global actions from the config
*/
private async getGlobalActions(): Promise<GlobalActions<PfeGlobalAction> | undefined> {
return (await this.pfeConfigurationService.getConfig())?.globalConfiguration?.actions;
}
/**
* Register the default actions included in the Pfe
*/
private registerDefaultActions() {
this.registerAction(PfeUpdateStateValuesType, this.pfeUpdateStateValuesService.executeActionUpdateStatesValues);
this.registerAction(PfeUpdateStateOnBackend, this.pfeUpdateStateOnBackendService.executeActionUpdateStateOnBackend);
this.registerAction(PfeRewindHistory, this.pfeRewindHistoryService.rewindHistory);
this.registerAction(ReloadPageActionType, reloadPageAction);
this.registerAction(PfeResetStateActionType, this.pfeResetStateService.resetState);
this.registerAction(PfeNestedActionsType, (options: PfeNestedActions<PfeActionConfig>) => this.executeActions(options.actions));
}
}