File

libs/ngx-pfe/pfe-dev-tools/pfe-dev-tools.component.ts

Implements

OnInit AfterViewInit

Metadata

Index

Properties
Methods
Inputs
Accessors

Constructor

constructor(pfeStateService: PfeStateService, pfeConfigurationService: PfeConfigurationService, pfeNavigationService: PfeNavigationService, pfeUtilService: PfeUtilService, cdRef: ChangeDetectorRef, pfeConditionsService: PfeConditionsService)
Parameters :
Name Type Optional
pfeStateService PfeStateService No
pfeConfigurationService PfeConfigurationService No
pfeNavigationService PfeNavigationService No
pfeUtilService PfeUtilService No
cdRef ChangeDetectorRef No
pfeConditionsService PfeConditionsService No

Inputs

beautifyJson
Type : boolean
Default value : false
devToolsActive
Type : boolean
Default value : false

Methods

buildForm
buildForm()
Returns : void
copyCurrentState
copyCurrentState()
Returns : void
evaluateCondition
evaluateCondition()
Returns : void
evaluateExpression
evaluateExpression()
Returns : void
goToPage
goToPage(pageID: string)
Parameters :
Name Type Optional
pageID string No
Returns : void
loadExampleConditionAdvanced
loadExampleConditionAdvanced()
Returns : void
loadExampleExpression
loadExampleExpression()
Returns : void
loadExampleExpressionCondition
loadExampleExpressionCondition()
Returns : void
logConditionEvaluation
logConditionEvaluation(parsedJSON: Record)
Parameters :
Name Type Optional
parsedJSON Record<string | > No
Returns : void
logConditionWithOperator
logConditionWithOperator(expressionCondition: ExpressionCondition, parsedJSON: Record, conditionIndex: number)
Parameters :
Name Type Optional
expressionCondition ExpressionCondition No
parsedJSON Record<string | > No
conditionIndex number No
Returns : any
parseConditions
parseConditions()
Returns : void
parseJsonPathData
parseJsonPathData()
Returns : void
resetState
resetState()
Returns : void
restoreFromInputField
restoreFromInputField()
Returns : void
restoreFromServer
restoreFromServer()
Returns : void
storeOnServer
storeOnServer()
Returns : void

Properties

conditionEvaluationLogs
Type : string | undefined
conditionParseError
Default value : false
conditionsResult
Type : boolean |
errorPageId
Type : PageId | undefined
fGroup
Type : UntypedFormGroup
hideConditionMessage
Default value : false
hideJsonPathMessage
Default value : false
isBeautifyJson
Type : boolean | undefined
json
Type : Record<string | > | undefined
jsonPathConditions
Type : string | undefined
jsonPathData
Type : string | undefined
jsonPathError
Default value : false
jsonPathExpression
Type : string | undefined
jsonPathResult
Type : string |
jsonQueryResult
Type : string | undefined
jsonStateData
Type : string | undefined
jsonValueResult
Type : string | undefined
pages
Type : PageNavigationConfiguration[] | undefined
Public pfeStateService
Type : PfeStateService
staleState
Type : Record<string | > | undefined

Accessors

exampleExpression
getexampleExpression()
exampleExpressionCondition
getexampleExpressionCondition()
exampleConditionAdvanced
getexampleConditionAdvanced()
import {
  ConditionAdvanced,
  ExpressionCondition,
  JSON_PATH_REGEX,
  OPERATORS,
  PFE_AND,
  PFE_EXPRESSION,
  PFE_OR,
  PageId,
  PageNavigationConfiguration,
  PfeConditionsNormalizeUtils,
  PfeConditionsService,
  PfeConfigurationService,
  PfeNavigationService,
  PfeRulesEvaluator,
  PfeStateService,
  PfeUserInputState,
  PfeUtilService,
} from '@allianz/ngx-pfe';
import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import jsonpath from 'jsonpath';
import { debounceTime, filter } from 'rxjs/operators';

const DEVTOOLSURLPARAM = 'devtools';
const EXAMPLE_EXPRESSION = '$.lastVisitedPage';
const EXAMPLE_EXPRESSION_CONDITION: ExpressionCondition[] = [
  { expression: '{$.pfeVisitedPages[:1]} === {$.lastVisitedPage}' },
  { value1Expression: '$.lastVisitedPage', operator: '==', value2Expression: '$.pfeVisitedPages[:1]' },
];
const EXAMPLE_CONDITION_ADVANCED: ConditionAdvanced = {
  operator: OPERATORS.AND,
  conditions: [
    { value1Expression: '$.lastVisitedPage', operator: '==', value2Expression: '$.pfeHistory[:1]' },
    { value1Expression: 5, operator: '==', value2Expression: 4 },
  ],
};

/**
 * @export
 */
@Component({
  selector: 'pfe-dev-tools',
  templateUrl: './pfe-dev-tools.component.html',
  styleUrls: ['./pfe-dev-tools.component.scss'],
})
export class DevToolsComponent implements OnInit, AfterViewInit {
  @Input() devToolsActive = false;
  @Input() beautifyJson = false;

  json: Record<string, unknown> | undefined;
  staleState: Record<string, unknown> | undefined;
  isBeautifyJson: boolean | undefined;
  jsonPathData: string | undefined;
  jsonPathExpression: string | undefined;
  jsonPathConditions: string | undefined;
  jsonPathResult: string | unknown;
  jsonQueryResult: string | undefined;
  jsonValueResult: string | undefined;
  conditionsResult: boolean | unknown;
  jsonStateData: string | undefined;
  conditionEvaluationLogs: string | undefined;
  pages: PageNavigationConfiguration[] | undefined;
  errorPageId: PageId | undefined;
  fGroup!: UntypedFormGroup;
  jsonPathError = false;
  conditionParseError = false;
  hideJsonPathMessage = false;
  hideConditionMessage = false;

  constructor(
    public pfeStateService: PfeStateService,
    private pfeConfigurationService: PfeConfigurationService,
    private pfeNavigationService: PfeNavigationService,
    private pfeUtilService: PfeUtilService,
    private cdRef: ChangeDetectorRef,
    private pfeConditionsService: PfeConditionsService
  ) {
    this.buildForm();
  }

  get exampleExpression() {
    return EXAMPLE_EXPRESSION;
  }

  get exampleExpressionCondition() {
    return JSON.stringify(EXAMPLE_EXPRESSION_CONDITION, null, 2);
  }

  get exampleConditionAdvanced() {
    return JSON.stringify(EXAMPLE_CONDITION_ADVANCED, null, 2);
  }

  ngOnInit(): void {
    const urlParam = this.pfeUtilService.getURLParam(DEVTOOLSURLPARAM);

    if (this.devToolsActive === true || (urlParam && urlParam === 'true')) {
      this.devToolsActive = true;

      this.pfeConfigurationService.getConfig().then((config) => {
        this.pages = config.navConfiguration.pages;
        this.errorPageId = config.navConfiguration.errorPageID;
      });
    }

    this.pfeStateService.pfeUserInputState$.pipe(debounceTime(500)).subscribe((value) => {
      this.json = value;
    });

    this.copyCurrentState();
  }

  ngAfterViewInit() {
    this.cdRef.detectChanges();
  }

  buildForm() {
    this.fGroup = new UntypedFormGroup({
      selectPage: new UntypedFormControl(),
    });

    this.fGroup.controls.selectPage.valueChanges.pipe(filter((value) => value !== null)).subscribe((value) => this.goToPage(value));
  }

  goToPage(pageID: string) {
    console.log(`Navigating directly to page: ${pageID}`);
    this.fGroup.controls.selectPage.reset(null);
    this.pfeNavigationService.navigate(pageID);
  }

  storeOnServer() {
    this.pfeStateService.storeState();
  }

  restoreFromServer() {
    this.pfeStateService.automaticallyRestoreState();
  }

  restoreFromInputField() {
    if (this.jsonStateData) {
      const state: PfeUserInputState = JSON.parse(this.jsonStateData);
      this.pfeStateService.restoreState(state);
    }
  }

  resetState() {
    this.pfeStateService.resetState();
  }

  copyCurrentState() {
    const state = this.pfeStateService.getFullState();
    this.jsonPathData = JSON.stringify(state);
    this.parseJsonPathData();
  }

  evaluateExpression() {
    this.jsonPathResult = '';
    this.jsonQueryResult = '';
    this.jsonValueResult = '';

    try {
      if (this.jsonPathData && this.jsonPathExpression) {
        const parsedJSON = JSON.parse(this.jsonPathData);
        const safeJsonPathExpression = this.jsonPathExpression.replace('[]$', '$');
        this.jsonPathResult = JSON.stringify(jsonpath.nodes(parsedJSON, safeJsonPathExpression), null, 2);

        this.jsonQueryResult = JSON.stringify(jsonpath.query(parsedJSON, safeJsonPathExpression), null, 2);

        this.jsonValueResult = JSON.stringify(PfeUtilService.getValueOrList(parsedJSON, this.jsonPathExpression, true), null, 2);
      }
    } catch (error) {
      this.jsonPathResult = error;
    }
  }

  evaluateCondition() {
    try {
      if (this.jsonPathData && this.jsonPathConditions) {
        const parsedJSON = JSON.parse(this.jsonPathData);
        this.conditionsResult = this.pfeConditionsService.evaluateConditions(JSON.parse(this.jsonPathConditions), parsedJSON);
        this.logConditionEvaluation(parsedJSON);
      }
    } catch (error) {
      this.conditionsResult = error;
    }
  }

  logConditionEvaluation(parsedJSON: Record<string, unknown>) {
    this.conditionEvaluationLogs = '';

    if (this.jsonPathData && this.jsonPathConditions) {
      const normalizedConditions = PfeConditionsNormalizeUtils.normalizeConditions(JSON.parse(this.jsonPathConditions));

      normalizedConditions.forEach((condition, index) => {
        // ConditionAdvanced
        if (condition.type === PFE_AND || condition.type === PFE_OR) {
          const conditionAdvanced = condition as unknown as ConditionAdvanced;
          this.conditionEvaluationLogs += `ConditionAdvanced ${condition.type}:\n\n`;
          this.conditionEvaluationLogs += '----------------------------------------------------------------\n\n';

          const evaluatedValues = conditionAdvanced.conditions.map((expressionCondition, conditionAdvancedIndex) => {
            const values = this.logConditionWithOperator(expressionCondition, parsedJSON, conditionAdvancedIndex);
            this.conditionEvaluationLogs += '----------------------------------------------------------------\n\n';
            return values;
          });
          const logicalOperator = condition.type === PFE_AND ? ' AND ' : ' OR ';
          const conditionsResult = this.pfeConditionsService.evaluateConditions(condition, parsedJSON);
          this.conditionEvaluationLogs += `${evaluatedValues.join(logicalOperator)} => ${conditionsResult} \n\n`;
        }

        if (condition.type === PFE_EXPRESSION) {
          const expressionCondition = condition as ExpressionCondition;

          // ExpressionCondition with "expression"
          if (typeof expressionCondition?.expression == 'string') {
            this.conditionEvaluationLogs += `Condition #${index}:\n`;

            const replacer = PfeRulesEvaluator.getReplacer(parsedJSON as PfeUserInputState);
            const jsonPathExpressions = expressionCondition.expression.match(JSON_PATH_REGEX)?.map((match) => match.slice(1, -1)) || [];
            this.conditionEvaluationLogs += jsonPathExpressions
              .map((jsonPathExpression: string) => `  - "${jsonPathExpression}" evaluates to ${replacer(jsonPathExpression)}\n`)
              .join('');

            const conditionsResult = this.pfeConditionsService.evaluateConditions(expressionCondition, parsedJSON);
            const evaluatedStatement = expressionCondition.expression.replace(JSON_PATH_REGEX, replacer);
            this.conditionEvaluationLogs += `\n  ${evaluatedStatement} => ${conditionsResult}\n\n`;
          }

          // ExpressionCondition with "value1Expression", "value2Expression" and "operator"
          else {
            this.logConditionWithOperator(expressionCondition, parsedJSON, index);
          }
          this.conditionEvaluationLogs += '----------------------------------------------------------------\n\n';
        }
      });
    }
  }

  logConditionWithOperator(expressionCondition: ExpressionCondition, parsedJSON: Record<string, unknown>, conditionIndex: number) {
    this.conditionEvaluationLogs += `Condition #${conditionIndex}:\n`;

    if (expressionCondition.value1Expression && expressionCondition.value2Expression && expressionCondition.operator) {
      const { value1Expression, value2Expression, operator } = expressionCondition;

      let evaluatedValue1Expression = value1Expression;
      let evaluatedValue2Expression = value2Expression;
      if (typeof value1Expression === 'string') {
        evaluatedValue1Expression = `"${PfeUtilService.getValueOrList(parsedJSON, value1Expression, true)}"`;
        this.conditionEvaluationLogs += `  - "${value1Expression}" evaluates to ${evaluatedValue1Expression}\n`;
      }
      if (typeof value2Expression === 'string') {
        evaluatedValue2Expression = `"${PfeUtilService.getValueOrList(parsedJSON, value2Expression, true)}"`;
        this.conditionEvaluationLogs += `  - "${value2Expression}" evaluates to ${evaluatedValue2Expression}\n`;
      }

      const conditionsResult = this.pfeConditionsService.evaluateConditions(expressionCondition, parsedJSON);
      this.conditionEvaluationLogs += `\n  ${evaluatedValue1Expression} ${operator} ${evaluatedValue2Expression} => ${conditionsResult}\n\n`;
      return conditionsResult;
    }
  }

  parseConditions() {
    try {
      if (this.jsonPathConditions) {
        this.jsonPathConditions = JSON.stringify(JSON.parse(this.jsonPathConditions), null, 2);
        this.conditionParseError = false;
      }
    } catch (e) {
      this.conditionParseError = true;
    }
  }

  parseJsonPathData() {
    try {
      if (this.jsonPathData) {
        this.jsonPathData = JSON.stringify(JSON.parse(this.jsonPathData), null, 2);
        this.jsonPathError = false;
      }
    } catch (e) {
      this.jsonPathError = true;
    }
  }

  loadExampleExpression() {
    this.jsonPathExpression = this.exampleExpression;
  }

  loadExampleExpressionCondition() {
    this.jsonPathConditions = this.exampleExpressionCondition;
  }

  loadExampleConditionAdvanced() {
    this.jsonPathConditions = this.exampleConditionAdvanced;
  }
}
<div nxLayout="grid nogutters" class="dev-tools-box" *ngIf="devToolsActive">
  <h1 nxHeadline="subsection-medium">Dev Tools</h1>

  <nx-tab-group appearance="expert">
    <nx-tab label="General">
      <div nxRow>
        <div nxCol="9,12,6,5">
          <h2 class="headline">State Storage</h2>
          <button nxButton="primary small" (click)="storeOnServer()" style="margin-right: 8px">Store</button>
          <button nxButton="secondary small" (click)="restoreFromServer()">Restore</button>
        </div>

        <div nxCol="9,12,6,6">
          <h2 class="headline">Directly Jump to Page</h2>
          <form [formGroup]="fGroup">
            <label for="select-page">Select Page ID: </label>
            <select formControlName="selectPage" name="select-page" id="cars">
              <option *ngFor="let page of pages" [value]="page.pageId">{{ page.pageId }}</option>
              <option *ngIf="errorPageId" [value]="errorPageId">{{ errorPageId }}</option>
            </select>
          </form>
        </div>
      </div>

      <div nxRow>
        <div nxCol="9,12,12,9">
          <h2 class="headline">Current data model</h2>
          <input type="checkbox" id="beautify-json-toggle" [(ngModel)]="isBeautifyJson" />
          <label for="beautify-json-toggle">Beautify JSON</label><br />

          <pre *ngIf="!isBeautifyJson">{{ json | json }}</pre>
          <ngx-json-viewer *ngIf="isBeautifyJson" [json]="json"></ngx-json-viewer>
          <form>
            <label class="headline" for="json-state-data">Restore state from JSON:</label><br />
            <textarea [(ngModel)]="jsonStateData" id="json-state-data" name="jsonStateData" class="form-control"></textarea>
            <p>
              <button nxButton="primary small" (click)="restoreFromInputField()">Restore</button>
            </p>
            <p>
              <button nxButton="primary small" (click)="resetState()">Reset State</button>
            </p>
          </form>
        </div>
      </div>
    </nx-tab>

    <nx-tab label="JSONPath Expressions">
      <h2 class="headline">Custom State</h2>
      <form>
        <div nxRow>
          <div nxCol="12,12,12,12">
            <label for="jsonpath-state-data">Json data:</label><br />
            <textarea (blur)="parseJsonPathData()" [(ngModel)]="jsonPathData" id="jsonpath-state-data"
                      name="jsonPathStateData" class="form-control"></textarea><br />
            <nx-error *ngIf="jsonPathError" appearance="text">Invalid JSON</nx-error>
          </div>
        </div>
        <p>
          <button nxButton="primary small" (click)="copyCurrentState()">Load current app state</button>
        </p>
        <h2 class="headline nx-margin-top-m">JSONPath Expressions</h2>
        <nx-message *ngIf="!hideConditionMessage"
                    context="info"
                    closable="true"
                    (close)="hideConditionMessage = true" context="info">Take a look at the
          <nx-link><a target="_blank"
                      href="https://ngx-pfe.frameworks.allianz.io/assets/docs/additional-documentation/state-storage.html#state-expressions">PFE
            documentation</a></nx-link>
          to learn more about
          <nx-link><a target="_blank" href="https://goessner.net/articles/JsonPath/">JSONPath</a></nx-link>
          .
        </nx-message>
        <label for="json-path-expression">Expression:</label><br />
        <input [placeholder]="exampleExpression" [(ngModel)]="jsonPathExpression" id="json-path-expression"
               name="jsonPathExpression" />
        <p>
          <button nxButton="primary small" (click)="evaluateExpression()">Evaluate expression</button>
          <button nxButton="secondary small" (click)="loadExampleExpression()">Load example</button>
        </p>
        <div nxRow>
          <div nxCol="12,12,12,12">
            <span>Value result:</span>
            <textarea readonly class="code-box" name="jsonValueResult">{{ jsonValueResult }}</textarea>
          </div>

          <details nxCol="12,12,12,12">
            <summary>Query & path result (for debugging):</summary>
            <div nxRow>
              <div nxCol="12,12,12,6">
                <span>Query result:</span>
                <textarea readonly class="code-box" name="jsonQueryResult">{{ jsonQueryResult }}</textarea>
              </div>

              <div nxCol="12,12,12,6">
                <span>Path result:</span>
                <textarea readonly class="code-box" name="jsonPathResult">{{ jsonPathResult }}</textarea>
              </div>
            </div>
          </details>
        </div>

        <div nxRow>
          <div nxCol="12,12,12,12">
            <form>
              <h2 class="headline nx-margin-top-m">ExpressionCondition Evaluation</h2>
              <nx-message
                *ngIf="!hideJsonPathMessage"
                context="info"
                closable="true"
                (close)="hideJsonPathMessage = true" context="info">Take a look at the PFE documentation to learn more
                about
                <nx-link><a target="_blank"
                            href="https://ngx-pfe.frameworks.allianz.io/assets/docs/additional-documentation/flow-and-backend-integration/pfe-conditions.html">PFE
                  Conditions</a></nx-link>
                in general,
                <nx-link><a target="_blank"
                            href="https://ngx-pfe.frameworks.allianz.io/assets/docs/interfaces/ExpressionCondition.html">ExpressionCondition</a>
                </nx-link>
                and
                <nx-link><a target="_blank"
                            href="https://ngx-pfe.frameworks.allianz.io/assets/docs/interfaces/ConditionAdvanced.html">ConditionAdvanced</a>
                </nx-link>
              </nx-message>
              <label for="json-path-conditions">Conditions (ExpressionCondition[] | ConditionAdvanced)</label><br />
              <textarea
                [(ngModel)]="jsonPathConditions"
                (blur)="parseConditions()"
                id="json-path-conditions"
                name="jsonPathConditions"
              ></textarea>
              <nx-error *ngIf="conditionParseError" appearance="text">Invalid JSON</nx-error>
              <p>
                <button nxButton="primary small" (click)="evaluateCondition()">Evaluate conditions</button>
                <button nxButton="secondary small" (click)="loadExampleExpressionCondition()">Load ExpressionCondition[]
                  example
                </button>
                <button nxButton="secondary small" (click)="loadExampleConditionAdvanced()">Load ConditionAdvanced
                  example
                </button>
              </p>
              <span>Result of conditions:</span>
              <textarea readonly class="code-box" name="conditionsResult">{{ conditionsResult }}</textarea>
              <details>
                <summary>Logs from the evaluation of the condition (for debugging):</summary>
                <textarea readonly class="code-box">{{ conditionEvaluationLogs }}</textarea>
              </details>
            </form>
          </div>
        </div>
      </form>
    </nx-tab>

    <nx-tab label="Actions">
      <pfe-dev-tools-actions />
    </nx-tab>
  </nx-tab-group>
</div>

./pfe-dev-tools.component.scss

.dev-tools-box {
  border: 1px solid var(--ui-04, #ddd);
  border-radius: 8px;
  margin: 0 0.5em 0.5em 0.5em;
  padding: 25px;
  font-size: 14px;
  line-height: 20px;
  background: var(--ui-01, #ffffff);
}

textarea {
  height: 100px;
  width: 100%;
  max-width: 100%;
}

input#json-path-expression {
  width: 100%;
  font-family: monospace;
}

button {
  margin-bottom: 8px;
}

pre {
  background: var(--ui-02, #f5f5f5);
  padding: 10px;
  overflow: auto;
}

h1.nx-heading--subsection-medium {
  margin-bottom: 24px;
}

.headline {
  font-size: 16px;
  line-height: 24px;
  font-weight: 600;
}

ngx-json-viewer ::ng-deep section.ngx-json-viewer {
  height: unset;
}

.code-box {
  font-family: monospace;
  height: 100px;
  font-size: 14px;
  line-height: 16px;
  margin: 0;
  border: 1px solid var(--ui-04, #d9d9d9);
  background: var(--ui-02, #f5f5f5);
  resize: both;
  overflow: auto;
  max-width: 100%;

  white-space-collapse: preserve;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""