File

libs/acl/angular/src/lib/inspector/inspector.component.ts

Metadata

Index

Properties
Methods
Inputs
HostListeners
Accessors

Constructor

constructor(inspectorService: AclInspectorService)
Parameters :
Name Type Optional
inspectorService AclInspectorService No

Inputs

persistPolicyUrl
Type : string
show
Type : boolean
Default value : false

HostListeners

document:keypress
Arguments : '$event'
document:keypress(event: KeyboardEvent)

Methods

handleContextChanged
handleContextChanged(value: string)
Parameters :
Name Type Optional
value string No
Returns : void
onKeyPress
onKeyPress(event: KeyboardEvent)
Decorators :
@HostListener('document:keypress', ['$event'])
Parameters :
Name Type Optional
event KeyboardEvent No
Returns : void
open
open(value?: boolean)
Parameters :
Name Type Optional
value boolean Yes
Returns : void
updateRules
updateRules(rules: AclRule[])
Parameters :
Name Type Optional
rules AclRule[] No
Returns : void

Properties

aclHintCheckboxControl
Default value : new UntypedFormControl(false)
aclIconName
Default value : AclIconName
selectedTab
Default value : this.tabs[0]
tabs
Type : []
Default value : ['Rules', 'Reports', 'Settings', 'Environment', 'Help']
template
Type : TemplateRef<>
Decorators :
@ViewChild(TemplateRef)

Accessors

aclDecisionLog
getaclDecisionLog()
aclService
getaclService()
import { AclDecision, AclRule } from '@allianz/taly-acl';
import {
  Component,
  DestroyRef,
  HostListener,
  Inject,
  Input,
  TemplateRef,
  ViewChild,
  inject
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { UntypedFormControl } from '@angular/forms';
import { startWith, tap } from 'rxjs/operators';
import { AclIconName } from '../acl-icon/acl-icon.interface';
import { AclService } from '../services/acl.service';
import { AclInspectorService } from './inspector.service';

// TODO: proper inspector resize could be achieved with https://stackoverflow.com/questions/56776046/how-to-make-angular-material-dialog-re-sizable-in-angular-7
@Component({
  selector: 'acl-inspector',
  templateUrl: './inspector.component.html',
  styleUrls: ['./inspector.component.scss'],
  standalone: false
})
export class AclInspectorComponent {
  aclIconName = AclIconName;
  aclHintCheckboxControl = new UntypedFormControl(false);
  tabs = ['Rules', 'Reports', 'Settings', 'Environment', 'Help'];
  selectedTab = this.tabs[0];

  @Input() show = false;
  @Input() persistPolicyUrl!: string;

  @ViewChild(TemplateRef) template!: TemplateRef<unknown>;
  private inspectorService: AclInspectorService;
  private destroyRef = inject(DestroyRef);

  constructor(@Inject(AclInspectorService) inspectorService: AclInspectorService) {
    this.inspectorService = inspectorService;
    this.inspectorService.openSignal.subscribe(() => this.open(true));

    this.aclHintCheckboxControl.valueChanges
      .pipe(
        takeUntilDestroyed(this.destroyRef),
        startWith(this.aclHintCheckboxControl.value),
        tap((showHints) => {
          this.inspectorService.showAclHints$.next(showHints);
        })
      )
      .subscribe();

    this.destroyRef.onDestroy(() => {
      this.inspectorService.showAclHints$.next(false);
    });
  }

  handleContextChanged(value: string) {
    this.aclService.setEnvironmentValue('context', value);
  }

  updateRules(rules: AclRule[]) {
    this.aclService._setGlobalRules(rules);
  }

  open(value?: boolean) {
    this.show = value ?? !this.show;
  }

  get aclDecisionLog(): AclDecision[] {
    return this.inspectorService.aclDecisionLog;
  }

  get aclService(): AclService {
    return this.inspectorService.aclService;
  }

  @HostListener('document:keypress', ['$event'])
  onKeyPress(event: KeyboardEvent) {
    // bail if input element is focused
    if (
      document.activeElement?.tagName === 'INPUT' ||
      document.activeElement?.tagName === 'TEXTAREA'
    ) {
      return;
    }

    if (event.key === 'h') {
      this.aclHintCheckboxControl.setValue(!this.aclHintCheckboxControl.value);
    }
  }
}
<span class="anchor" cdkOverlayOrigin #trigger="cdkOverlayOrigin"></span>
<button class="debug-opener" (click)="open()">
  <acl-icon [name]="aclIconName.Acl" size="28"></acl-icon> ACL Inspector
  <span class="triangle"><span class="triangle-down"></span></span>
</button>

<ng-template
  cdkConnectedOverlay
  (detach)="show = false"
  [cdkConnectedOverlayOrigin]="trigger"
  [cdkConnectedOverlayOpen]="show"
>
  <div cdkDrag class="debug-panel" cdkDragRootElement=".cdk-overlay-pane">
    <div class="debug-content">
      <debug-header (closeWindow)="open(false)" cdkDragHandle></debug-header>

      <div class="tab-group-nav padding-left-16">
        <button
          class="tab-btn"
          [ngClass]="{ 'tab-btn--active': selectedTab === tab }"
          *ngFor="let tab of tabs; let i = index"
          (click)="selectedTab = tab"
        >
          {{ tab }}
        </button>
      </div>

      <div class="tab-group">
        <div class="tab" *ngIf="selectedTab === 'Rules'">
          <acl-policy-editor
            class="editor"
            (changed)="updateRules($event)"
            [policy]="(this.aclService.currentPolicy$ | async)!"
            [persistPolicyUrl]="this.persistPolicyUrl"
          >
          </acl-policy-editor>
        </div>

        <div class="tab" *ngIf="selectedTab === 'Reports'">
          <report-viewer
            (ruleAdded)="aclService._addGlobalRule($event)"
            [log]="aclDecisionLog"
          ></report-viewer>
        </div>

        <div class="tab" *ngIf="selectedTab === 'Settings'" class="tab-content tab-content-padding">
          <label>
            <input type="checkbox" [formControl]="aclHintCheckboxControl" />
            Show Acl Hints
          </label>
        </div>

        <div
          class="tab"
          *ngIf="selectedTab === 'Environment'"
          class="tab-content tab-content-padding"
        >
          <acl-environment context="creation" (contextChanged)="handleContextChanged($event)">
          </acl-environment>
        </div>

        <div class="tab" *ngIf="selectedTab === 'Help'" class="tab-content tab-content-padding">
          <p>
            Rules are evaluated with the <strong>"first-applicable"</strong> strategy.<br />
            This means a given resource is checked from top to bottom against all policy rules.<br />
            The first one matching determines the state of the resource. If no rule is matching, a
            resource by default is visible and neither disabled nor readonly.<br />
            <br />
            - Try to double click to edit a rule part
            <br />
            - Rearrange rules with the handle on the left to test the order against the algorithm
          </p>
        </div>
      </div>
    </div>
  </div>
</ng-template>

./inspector.component.scss

@use '../inspector.shared' as *;
@use '../inspector.colors' as *;

:host {
  display: block;
  position: fixed;
  right: 25px;
  top: 60px;
  z-index: 10;
}

.anchor {
  position: absolute;
  right: 30px; /**button width**/
  top: 5px;
  z-index: -1;
}

.debug-panel {
  background-color: $debug-color-dark;
  color: #fff;
  padding: 2px 0;
  line-height: 16px;
  font-size: 13px;
  border-radius: 8px;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12),
    0 3px 1px -2px rgba(0, 0, 0, 0.2);
  overflow: hidden;
}

.debug-opener {
  cursor: pointer;
  font-size: 20px;
  line-height: 24px;
  padding: 8px 16px 8px 6px;
  margin: 0 -20px 0 10px;
  background-color: $debug-color-dark;
  border: 1px solid $debug-color-dark;
  color: #fff;
  border-radius: 8px;
  font-weight: 700;
  vertical-align: middle;
  display: inline-block;
  transition: background-color 0.3s ease;
  transform: rotate(90deg) translate(102%);
  transform-origin: top right;
  &:hover,
  &:focus {
    background-color: $debug-color-dark-hover;
  }
  svg {
    margin-bottom: -6px;
  }
  .triangle {
    margin-bottom: -1px;
  }
}

.tab-group-nav {
  background-color: $debug-color-dark;
  padding-top: 8px;
}
.tab-btn {
  @include base-button-styles;
  display: inline-block;
  padding: 8px 16px;
  font-size: 16px;
  line-height: 18px;
  font-weight: 700;
  border-top-left-radius: 4px;
  border-top-right-radius: 4px;
  margin-right: 1px;
  color: #fff;

  &:hover,
  &--active {
    background-color: $debug-color-light;
  }
}
.tab-group {
  background-color: $debug-color-light;
}
.tab-content {
  font-size: 14px;
}
.tab-content-padding {
  padding: 16px;
}

.editor {
  height: 50vh;
  width: 50vw;
  resize: both;
  overflow-y: auto;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""