File

libs/acl/src/lib/acl-tag/acl-tag.ts

Index

Properties
Methods
Accessors

Constructor

constructor(tagName: BehaviorSubject | string, parent?: AclTag, transient?: BehaviorSubject)
Parameters :
Name Type Optional
tagName BehaviorSubject<string> | string No
parent AclTag Yes
transient BehaviorSubject<boolean> Yes

Properties

_aclKeySubject
Type : ReplaySubject<AclTagItem>
Default value : new ReplaySubject<AclTagItem>(1)
tagName$
Type : BehaviorSubject<string>
transient$
Type : BehaviorSubject<boolean>
Default value : new BehaviorSubject<boolean>(false)

Methods

destroy
destroy()
Returns : void
Static joinKeys
joinKeys(...keys: string[])
Parameters :
Name Type Optional
keys string[] No
Returns : any
notifyChildren
notifyChildren()
Returns : void
subscribeToChanges
subscribeToChanges()

wait for parent changes or own tag name changes and calculate tag name for each change

Returns : void
update
update()
Returns : void

Accessors

aclKey$
getaclKey$()
changed$
getchanged$()
aclKey
getaclKey()
ownTag
getownTag()
isTransient
getisTransient()
tagList
gettagList()
parentList
getparentList()
import { BehaviorSubject, combineLatest, ReplaySubject, Subject } from 'rxjs';
import { startWith, takeUntil, tap } from 'rxjs/operators';

/**
 * Core element of our hierarchical acl tag toolkit.
 * Each time we find an element with a aclTag the bound directive (AclTagDirective)
 * creates an instance of this AclTag and injects it as 'ACL_TAG_TOKEN'.
 *
 * The created instance has a binding to the property 'aclTag' of the directive
 * and to any parent AclTag which builds up the expected hierarchy.
 */

export interface AclTagItem {
  ownKey: string;
  parentKeys: string[];
  aclKey: string /** full qualified path **/;
}

export class AclTag {
  tagName$: BehaviorSubject<string>;
  transient$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
  _aclKeySubject: ReplaySubject<AclTagItem> = new ReplaySubject<AclTagItem>(1);

  private _changed$ = new Subject<void>();
  private _destroySignal$ = new Subject<void>();
  static joinKeys(...keys: string[]) {
    return keys.join('/');
  }

  constructor(
    tagName: BehaviorSubject<string> | string,
    private parent?: AclTag,
    transient?: BehaviorSubject<boolean>
  ) {
    if (typeof tagName === 'string') {
      tagName = new BehaviorSubject(tagName);
    }
    if (transient) {
      this.transient$ = transient;
    }

    this.tagName$ = tagName;
    this.subscribeToChanges();
  }

  get aclKey$() {
    return this._aclKeySubject.asObservable().pipe(takeUntil(this._destroySignal$));
  }

  destroy() {
    this._destroySignal$.next();
  }

  get changed$() {
    return this._changed$.asObservable();
  }

  /**
   * wait for parent changes or own tag name changes and
   * calculate tag name for each change
   */
  subscribeToChanges() {
    const parentList = (this.parent?.changed$ ?? new Subject<void>()).pipe(startWith([null]));

    combineLatest([this.tagName$, parentList, this.transient$])
      .pipe(
        tap(() => {
          this.update();
        }),
        takeUntil(this._destroySignal$)
      )
      .subscribe();
  }

  notifyChildren() {
    this._changed$.next();
  }

  update() {
    // write current acl key intro the stream
    this._aclKeySubject.next({
      aclKey: this.aclKey,
      ownKey: this.ownTag,
      parentKeys: this.parentList
    });
    // notify dependent acl tags
    this.notifyChildren();
  }

  get aclKey() {
    if (this.isTransient) {
      return AclTag.joinKeys(...this.parentList, this.ownTag);
    }
    return AclTag.joinKeys(...this.tagList);
  }

  get ownTag() {
    return this.tagName$.getValue();
  }

  get isTransient() {
    return this.transient$.getValue();
  }

  get tagList(): string[] {
    if (this.isTransient) {
      return this.parentList;
    }
    return this.parentList.concat(this.ownTag);
  }

  get parentList() {
    return [...(this.parent?.tagList ?? [])];
  }
}

results matching ""

    No results matching ""