File

libs/ngx-pfe/pfe-actions/pfe-actions.service.ts

Index

Properties
Methods

Constructor

constructor(pfeStateService: PfeStateService, pfeUpdateStateValuesService: PfeUpdateStateValuesService, pfeUpdateStateOnBackendService: PfeUpdateStateOnBackendService, pfeConfigurationService: PfeConfigurationService, pfeRewindHistoryService: PfeRewindHistoryService, pfeResetStateService: PfeResetStateService, pfeConditionsService: PfeConditionsService, logger: NgxLoggerService)
Parameters :
Name Type Optional
pfeStateService PfeStateService No
pfeUpdateStateValuesService PfeUpdateStateValuesService No
pfeUpdateStateOnBackendService PfeUpdateStateOnBackendService No
pfeConfigurationService PfeConfigurationService No
pfeRewindHistoryService PfeRewindHistoryService No
pfeResetStateService PfeResetStateService No
pfeConditionsService PfeConditionsService No
logger NgxLoggerService No

Methods

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 :
Name Type Optional Description
actionCfg PfeBaseActionConfig No

Action configuration

interruptBusyState function Yes

Function to call to interrupt PFE's busy state if an action is waiting for user input

Returns : Promise<boolean>
Public Async executeActions
executeActions(actions: PfeBaseActionConfig[], interruptBusyState?: (value?: boolean) => void)

Execute an array of actions

Parameters :
Name Type Optional Description
actions PfeBaseActionConfig[] No

The array of action

interruptBusyState function Yes

Optional callback to interrupt PFE's busy state if an action is marked as notBusyWhileRunning

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 :
Name Type Optional Description
type string No

The name of the action

action PfeActionFunction No

The action itself, should be an arrow async function, see the default actions to see examples

options RegisterActionOptions Yes

Further options for the action.

Returns : void

Properties

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));
  }
}

results matching ""

    No results matching ""